L2Multisell.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package net.sf.l2j.gameserver.model;
  16. import java.io.File;
  17. import java.util.List;
  18. import java.util.logging.Level;
  19. import java.util.logging.Logger;
  20. import javax.xml.parsers.DocumentBuilderFactory;
  21. import javolution.util.FastList;
  22. import net.sf.l2j.Config;
  23. import net.sf.l2j.gameserver.datatables.ItemTable;
  24. import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
  25. import net.sf.l2j.gameserver.network.serverpackets.MultiSellList;
  26. import net.sf.l2j.gameserver.templates.item.L2Armor;
  27. import net.sf.l2j.gameserver.templates.item.L2Item;
  28. import net.sf.l2j.gameserver.templates.item.L2Weapon;
  29. import org.w3c.dom.Document;
  30. import org.w3c.dom.Node;
  31. /**
  32. * Multisell list manager
  33. *
  34. */
  35. public class L2Multisell
  36. {
  37. private static Logger _log = Logger.getLogger(L2Multisell.class.getName());
  38. private List<MultiSellListContainer> _entries = new FastList<MultiSellListContainer>();
  39. private static L2Multisell _instance = new L2Multisell();
  40. public MultiSellListContainer getList(int id)
  41. {
  42. synchronized (_entries)
  43. {
  44. for (MultiSellListContainer list : _entries)
  45. {
  46. if (list.getListId() == id) return list;
  47. }
  48. }
  49. _log.warning("[L2Multisell] can't find list with id: " + id);
  50. return null;
  51. }
  52. public L2Multisell()
  53. {
  54. parseData();
  55. }
  56. public void reload()
  57. {
  58. parseData();
  59. }
  60. public static L2Multisell getInstance()
  61. {
  62. return _instance;
  63. }
  64. private void parseData()
  65. {
  66. _entries.clear();
  67. parse();
  68. }
  69. /**
  70. * This will generate the multisell list for the items. There exist various
  71. * parameters in multisells that affect the way they will appear:
  72. * 1) inventory only:
  73. * * if true, only show items of the multisell for which the
  74. * "primary" ingredients are already in the player's inventory. By "primary"
  75. * ingredients we mean weapon and armor.
  76. * * if false, show the entire list.
  77. * 2) maintain enchantment: presumably, only lists with "inventory only" set to true
  78. * should sometimes have this as true. This makes no sense otherwise...
  79. * * If true, then the product will match the enchantment level of the ingredient.
  80. * if the player has multiple items that match the ingredient list but the enchantment
  81. * levels differ, then the entries need to be duplicated to show the products and
  82. * ingredients for each enchantment level.
  83. * For example: If the player has a crystal staff +1 and a crystal staff +3 and goes
  84. * to exchange it at the mammon, the list should have all exchange possibilities for
  85. * the +1 staff, followed by all possibilities for the +3 staff.
  86. * * If false, then any level ingredient will be considered equal and product will always
  87. * be at +0
  88. * 3) apply taxes: Uses the "taxIngredient" entry in order to add a certain amount of adena to the ingredients
  89. *
  90. * @see net.sf.l2j.gameserver.serverpackets.ServerBasePacket#runImpl()
  91. */
  92. private MultiSellListContainer generateMultiSell(int listId, boolean inventoryOnly, L2PcInstance player, double taxRate)
  93. {
  94. MultiSellListContainer listTemplate = L2Multisell.getInstance().getList(listId);
  95. MultiSellListContainer list = new MultiSellListContainer();
  96. if (listTemplate == null) return list;
  97. list = L2Multisell.getInstance().new MultiSellListContainer();
  98. list.setListId(listId);
  99. if (inventoryOnly)
  100. {
  101. if (player == null)
  102. return list;
  103. L2ItemInstance[] items;
  104. if (listTemplate.getMaintainEnchantment())
  105. items = player.getInventory().getUniqueItemsByEnchantLevel(false,false,false);
  106. else
  107. items = player.getInventory().getUniqueItems(false,false,false);
  108. int enchantLevel;
  109. for (L2ItemInstance item : items)
  110. {
  111. // only do the matchup on equipable items that are not currently equipped
  112. // so for each appropriate item, produce a set of entries for the multisell list.
  113. if (!item.isWear() && ((item.getItem() instanceof L2Armor) || (item.getItem() instanceof L2Weapon)))
  114. {
  115. enchantLevel = (listTemplate.getMaintainEnchantment()? item.getEnchantLevel() : 0);
  116. // loop through the entries to see which ones we wish to include
  117. for (MultiSellEntry ent : listTemplate.getEntries())
  118. {
  119. boolean doInclude = false;
  120. // check ingredients of this entry to see if it's an entry we'd like to include.
  121. for (MultiSellIngredient ing : ent.getIngredients())
  122. {
  123. if (item.getItemId() == ing.getItemId())
  124. {
  125. doInclude = true;
  126. break;
  127. }
  128. }
  129. // manipulate the ingredients of the template entry for this particular instance shown
  130. // i.e: Assign enchant levels and/or apply taxes as needed.
  131. if (doInclude)
  132. list.addEntry(prepareEntry(ent, listTemplate.getApplyTaxes(), listTemplate.getMaintainEnchantment(), enchantLevel, taxRate));
  133. }
  134. }
  135. } // end for each inventory item.
  136. } // end if "inventory-only"
  137. else // this is a list-all type
  138. {
  139. // if no taxes are applied, no modifications are needed
  140. for (MultiSellEntry ent : listTemplate.getEntries())
  141. list.addEntry(prepareEntry(ent, listTemplate.getApplyTaxes(), false, 0, taxRate));
  142. }
  143. return list;
  144. }
  145. // Regarding taxation, the following is the case:
  146. // a) The taxes come out purely from the adena TaxIngredient
  147. // b) If the entry has no adena ingredients other than the taxIngredient, the resulting
  148. // amount of adena is appended to the entry
  149. // c) If the entry already has adena as an entry, the taxIngredient is used in order to increase
  150. // the count for the existing adena ingredient
  151. private MultiSellEntry prepareEntry(MultiSellEntry templateEntry, boolean applyTaxes, boolean maintainEnchantment, int enchantLevel, double taxRate)
  152. {
  153. MultiSellEntry newEntry = L2Multisell.getInstance().new MultiSellEntry();
  154. newEntry.setEntryId(templateEntry.getEntryId()*100000+enchantLevel);
  155. int adenaAmount = 0;
  156. for (MultiSellIngredient ing : templateEntry.getIngredients())
  157. {
  158. // load the ingredient from the template
  159. MultiSellIngredient newIngredient = L2Multisell.getInstance().new MultiSellIngredient(ing);
  160. // if taxes are to be applied, modify/add the adena count based on the template adena/ancient adena count
  161. if ( ing.getItemId() == 57 && ing.isTaxIngredient() )
  162. {
  163. if (applyTaxes)
  164. adenaAmount += (int)Math.round(ing.getItemCount()*taxRate);
  165. continue; // do not adena yet, as non-taxIngredient adena entries might occur next (order not guaranteed)
  166. }
  167. else if ( ing.getItemId() == 57 ) // && !ing.isTaxIngredient()
  168. {
  169. adenaAmount += ing.getItemCount();
  170. continue; // do not adena yet, as taxIngredient adena entries might occur next (order not guaranteed)
  171. }
  172. // if it is an armor/weapon, modify the enchantment level appropriately, if necessary
  173. // not used for clan reputation and fame
  174. else if (maintainEnchantment && newIngredient.getItemId() > 0)
  175. {
  176. L2Item tempItem = ItemTable.getInstance().createDummyItem(ing.getItemId()).getItem();
  177. if ((tempItem instanceof L2Armor) || (tempItem instanceof L2Weapon))
  178. newIngredient.setEnchantmentLevel(enchantLevel);
  179. }
  180. // finally, add this ingredient to the entry
  181. newEntry.addIngredient(newIngredient);
  182. }
  183. // now add the adena, if any.
  184. if (adenaAmount > 0 )
  185. {
  186. newEntry.addIngredient(L2Multisell.getInstance().new MultiSellIngredient(57,adenaAmount,0,false,false));
  187. }
  188. // Now modify the enchantment level of products, if necessary
  189. for (MultiSellIngredient ing : templateEntry.getProducts())
  190. {
  191. // load the ingredient from the template
  192. MultiSellIngredient newIngredient = L2Multisell.getInstance().new MultiSellIngredient(ing);
  193. if (maintainEnchantment)
  194. {
  195. // if it is an armor/weapon, modify the enchantment level appropriately
  196. // (note, if maintain enchantment is "false" this modification will result to a +0)
  197. L2Item tempItem = ItemTable.getInstance().createDummyItem(ing.getItemId()).getItem();
  198. if ((tempItem instanceof L2Armor) || (tempItem instanceof L2Weapon))
  199. newIngredient.setEnchantmentLevel(enchantLevel);
  200. }
  201. newEntry.addProduct(newIngredient);
  202. }
  203. return newEntry;
  204. }
  205. public void separateAndSend(int listId, L2PcInstance player, boolean inventoryOnly, double taxRate)
  206. {
  207. MultiSellListContainer list = generateMultiSell(listId, inventoryOnly, player, taxRate);
  208. MultiSellListContainer temp = new MultiSellListContainer();
  209. int page = 1;
  210. temp.setListId(list.getListId());
  211. for (MultiSellEntry e : list.getEntries())
  212. {
  213. if (temp.getEntries().size() == 40)
  214. {
  215. player.sendPacket(new MultiSellList(temp, page++, 0));
  216. temp = new MultiSellListContainer();
  217. temp.setListId(list.getListId());
  218. }
  219. temp.addEntry(e);
  220. }
  221. player.sendPacket(new MultiSellList(temp, page, 1));
  222. }
  223. public class MultiSellEntry
  224. {
  225. private int _entryId;
  226. private List<MultiSellIngredient> _products = new FastList<MultiSellIngredient>();
  227. private List<MultiSellIngredient> _ingredients = new FastList<MultiSellIngredient>();
  228. /**
  229. * @param entryId The entryId to set.
  230. */
  231. public void setEntryId(int entryId)
  232. {
  233. _entryId = entryId;
  234. }
  235. /**
  236. * @return Returns the entryId.
  237. */
  238. public int getEntryId()
  239. {
  240. return _entryId;
  241. }
  242. /**
  243. * @param product The product to add.
  244. */
  245. public void addProduct(MultiSellIngredient product)
  246. {
  247. _products.add(product);
  248. }
  249. /**
  250. * @return Returns the products.
  251. */
  252. public List<MultiSellIngredient> getProducts()
  253. {
  254. return _products;
  255. }
  256. /**
  257. * @param ingredients The ingredients to set.
  258. */
  259. public void addIngredient(MultiSellIngredient ingredient)
  260. {
  261. _ingredients.add(ingredient);
  262. }
  263. /**
  264. * @return Returns the ingredients.
  265. */
  266. public List<MultiSellIngredient> getIngredients()
  267. {
  268. return _ingredients;
  269. }
  270. }
  271. public class MultiSellIngredient
  272. {
  273. private int _itemId, _itemCount, _enchantmentLevel;
  274. private boolean _isTaxIngredient, _mantainIngredient;
  275. public MultiSellIngredient(int itemId, int itemCount, boolean isTaxIngredient, boolean mantainIngredient)
  276. {
  277. this(itemId, itemCount, 0, isTaxIngredient, mantainIngredient);
  278. }
  279. public MultiSellIngredient(int itemId, int itemCount, int enchantmentLevel, boolean isTaxIngredient, boolean mantainIngredient)
  280. {
  281. setItemId(itemId);
  282. setItemCount(itemCount);
  283. setEnchantmentLevel(enchantmentLevel);
  284. setIsTaxIngredient(isTaxIngredient);
  285. setMantainIngredient(mantainIngredient);
  286. }
  287. public MultiSellIngredient(MultiSellIngredient e)
  288. {
  289. _itemId = e.getItemId();
  290. _itemCount = e.getItemCount();
  291. _enchantmentLevel = e.getEnchantmentLevel();
  292. _isTaxIngredient = e.isTaxIngredient();
  293. _mantainIngredient = e.getMantainIngredient();
  294. }
  295. /**
  296. * @param itemId The itemId to set.
  297. */
  298. public void setItemId(int itemId)
  299. {
  300. _itemId = itemId;
  301. }
  302. /**
  303. * @return Returns the itemId.
  304. */
  305. public int getItemId()
  306. {
  307. return _itemId;
  308. }
  309. /**
  310. * @param itemCount The itemCount to set.
  311. */
  312. public void setItemCount(int itemCount)
  313. {
  314. _itemCount = itemCount;
  315. }
  316. /**
  317. * @return Returns the itemCount.
  318. */
  319. public int getItemCount()
  320. {
  321. return _itemCount;
  322. }
  323. /**
  324. * @param itemCount The itemCount to set.
  325. */
  326. public void setEnchantmentLevel(int enchantmentLevel)
  327. {
  328. _enchantmentLevel = enchantmentLevel;
  329. }
  330. /**
  331. * @return Returns the itemCount.
  332. */
  333. public int getEnchantmentLevel()
  334. {
  335. return _enchantmentLevel;
  336. }
  337. public void setIsTaxIngredient(boolean isTaxIngredient)
  338. {
  339. _isTaxIngredient = isTaxIngredient;
  340. }
  341. public boolean isTaxIngredient()
  342. {
  343. return _isTaxIngredient;
  344. }
  345. public void setMantainIngredient(boolean mantainIngredient)
  346. {
  347. _mantainIngredient = mantainIngredient;
  348. }
  349. public boolean getMantainIngredient()
  350. {
  351. return _mantainIngredient;
  352. }
  353. }
  354. public class MultiSellListContainer
  355. {
  356. private int _listId;
  357. private boolean _applyTaxes = false;
  358. private boolean _maintainEnchantment = false;
  359. List<MultiSellEntry> _entriesC;
  360. public MultiSellListContainer()
  361. {
  362. _entriesC = new FastList<MultiSellEntry>();
  363. }
  364. /**
  365. * @param listId The listId to set.
  366. */
  367. public void setListId(int listId)
  368. {
  369. _listId = listId;
  370. }
  371. public void setApplyTaxes(boolean applyTaxes)
  372. {
  373. _applyTaxes = applyTaxes;
  374. }
  375. public void setMaintainEnchantment(boolean maintainEnchantment)
  376. {
  377. _maintainEnchantment = maintainEnchantment;
  378. }
  379. /**
  380. * @return Returns the listId.
  381. */
  382. public int getListId()
  383. {
  384. return _listId;
  385. }
  386. public boolean getApplyTaxes()
  387. {
  388. return _applyTaxes;
  389. }
  390. public boolean getMaintainEnchantment()
  391. {
  392. return _maintainEnchantment;
  393. }
  394. public void addEntry(MultiSellEntry e)
  395. {
  396. _entriesC.add(e);
  397. }
  398. public List<MultiSellEntry> getEntries()
  399. {
  400. return _entriesC;
  401. }
  402. }
  403. private void hashFiles(String dirname, List<File> hash)
  404. {
  405. File dir = new File(Config.DATAPACK_ROOT, "data/" + dirname);
  406. if (!dir.exists())
  407. {
  408. _log.config("Dir " + dir.getAbsolutePath() + " not exists");
  409. return;
  410. }
  411. File[] files = dir.listFiles();
  412. for (File f : files)
  413. {
  414. if (f.getName().endsWith(".xml")) hash.add(f);
  415. }
  416. }
  417. private void parse()
  418. {
  419. Document doc = null;
  420. int id = 0;
  421. List<File> files = new FastList<File>();
  422. hashFiles("multisell", files);
  423. for (File f : files)
  424. {
  425. id = Integer.parseInt(f.getName().replaceAll(".xml", ""));
  426. try
  427. {
  428. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  429. factory.setValidating(false);
  430. factory.setIgnoringComments(true);
  431. doc = factory.newDocumentBuilder().parse(f);
  432. }
  433. catch (Exception e)
  434. {
  435. _log.log(Level.SEVERE, "Error loading file " + f, e);
  436. }
  437. try
  438. {
  439. MultiSellListContainer list = parseDocument(doc);
  440. list.setListId(id);
  441. _entries.add(list);
  442. }
  443. catch (Exception e)
  444. {
  445. _log.log(Level.SEVERE, "Error in file " + f, e);
  446. }
  447. }
  448. }
  449. protected MultiSellListContainer parseDocument(Document doc)
  450. {
  451. MultiSellListContainer list = new MultiSellListContainer();
  452. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  453. {
  454. if ("list".equalsIgnoreCase(n.getNodeName()))
  455. {
  456. Node attribute;
  457. attribute = n.getAttributes().getNamedItem("applyTaxes");
  458. if(attribute == null)
  459. list.setApplyTaxes(false);
  460. else
  461. list.setApplyTaxes(Boolean.parseBoolean(attribute.getNodeValue()));
  462. attribute = n.getAttributes().getNamedItem("maintainEnchantment");
  463. if(attribute == null)
  464. list.setMaintainEnchantment(false);
  465. else
  466. list.setMaintainEnchantment(Boolean.parseBoolean(attribute.getNodeValue()));
  467. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  468. {
  469. if ("item".equalsIgnoreCase(d.getNodeName()))
  470. {
  471. MultiSellEntry e = parseEntry(d);
  472. list.addEntry(e);
  473. }
  474. }
  475. }
  476. else if ("item".equalsIgnoreCase(n.getNodeName()))
  477. {
  478. MultiSellEntry e = parseEntry(n);
  479. list.addEntry(e);
  480. }
  481. }
  482. return list;
  483. }
  484. protected MultiSellEntry parseEntry(Node n)
  485. {
  486. int entryId = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
  487. Node first = n.getFirstChild();
  488. MultiSellEntry entry = new MultiSellEntry();
  489. for (n = first; n != null; n = n.getNextSibling())
  490. {
  491. if ("ingredient".equalsIgnoreCase(n.getNodeName()))
  492. {
  493. Node attribute;
  494. int id = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
  495. int count = Integer.parseInt(n.getAttributes().getNamedItem("count").getNodeValue());
  496. boolean isTaxIngredient = false, mantainIngredient = false;
  497. attribute = n.getAttributes().getNamedItem("isTaxIngredient");
  498. if (attribute != null)
  499. isTaxIngredient = Boolean.parseBoolean(attribute.getNodeValue());
  500. attribute = n.getAttributes().getNamedItem("mantainIngredient");
  501. if (attribute != null)
  502. mantainIngredient = Boolean.parseBoolean(attribute.getNodeValue());
  503. MultiSellIngredient e = new MultiSellIngredient(id, count, isTaxIngredient, mantainIngredient);
  504. entry.addIngredient(e);
  505. }
  506. else if ("production".equalsIgnoreCase(n.getNodeName()))
  507. {
  508. int id = Integer.parseInt(n.getAttributes().getNamedItem("id").getNodeValue());
  509. int count = Integer.parseInt(n.getAttributes().getNamedItem("count").getNodeValue());
  510. MultiSellIngredient e = new MultiSellIngredient(id, count, false, false);
  511. entry.addProduct(e);
  512. }
  513. }
  514. entry.setEntryId(entryId);
  515. return entry;
  516. }
  517. }