ItemContainer.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. /*
  2. * Copyright (C) 2004-2014 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.model.itemcontainer;
  20. import java.sql.Connection;
  21. import java.sql.PreparedStatement;
  22. import java.sql.ResultSet;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import java.util.logging.Level;
  26. import java.util.logging.Logger;
  27. import javolution.util.FastList;
  28. import com.l2jserver.Config;
  29. import com.l2jserver.L2DatabaseFactory;
  30. import com.l2jserver.gameserver.GameTimeController;
  31. import com.l2jserver.gameserver.datatables.ItemTable;
  32. import com.l2jserver.gameserver.enums.ItemLocation;
  33. import com.l2jserver.gameserver.model.L2World;
  34. import com.l2jserver.gameserver.model.actor.L2Character;
  35. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  36. import com.l2jserver.gameserver.model.items.L2Item;
  37. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  38. /**
  39. * @author Advi
  40. */
  41. public abstract class ItemContainer
  42. {
  43. protected static final Logger _log = Logger.getLogger(ItemContainer.class.getName());
  44. protected final List<L2ItemInstance> _items = new FastList<L2ItemInstance>().shared();
  45. protected ItemContainer()
  46. {
  47. }
  48. protected abstract L2Character getOwner();
  49. protected abstract ItemLocation getBaseLocation();
  50. public String getName()
  51. {
  52. return "ItemContainer";
  53. }
  54. /**
  55. * @return int the owner object Id
  56. */
  57. public int getOwnerId()
  58. {
  59. return getOwner() == null ? 0 : getOwner().getObjectId();
  60. }
  61. /**
  62. * @return the quantity of items in the inventory
  63. */
  64. public int getSize()
  65. {
  66. return _items.size();
  67. }
  68. /**
  69. * @return the items in inventory
  70. */
  71. public L2ItemInstance[] getItems()
  72. {
  73. return _items.toArray(new L2ItemInstance[_items.size()]);
  74. }
  75. /**
  76. * @param itemId the item Id
  77. * @return the item from inventory by itemId
  78. */
  79. public L2ItemInstance getItemByItemId(int itemId)
  80. {
  81. for (L2ItemInstance item : _items)
  82. {
  83. if ((item != null) && (item.getId() == itemId))
  84. {
  85. return item;
  86. }
  87. }
  88. return null;
  89. }
  90. /**
  91. * @return true if player got item for self resurrection
  92. */
  93. public final boolean haveItemForSelfResurrection()
  94. {
  95. for (L2ItemInstance item : _items)
  96. {
  97. if ((item != null) && (item.getItem().isAllowSelfResurrection()))
  98. {
  99. return true;
  100. }
  101. }
  102. return false;
  103. }
  104. /**
  105. * @param itemId the item Id
  106. * @return the items list from inventory by using its itemId
  107. */
  108. public List<L2ItemInstance> getItemsByItemId(int itemId)
  109. {
  110. final List<L2ItemInstance> returnList = new ArrayList<>();
  111. for (L2ItemInstance item : _items)
  112. {
  113. if ((item != null) && (item.getId() == itemId))
  114. {
  115. returnList.add(item);
  116. }
  117. }
  118. return returnList;
  119. }
  120. /**
  121. * @param itemId the item Id
  122. * @param itemToIgnore used during the loop, to avoid returning the same item
  123. * @return the item from inventory by itemId
  124. */
  125. public L2ItemInstance getItemByItemId(int itemId, L2ItemInstance itemToIgnore)
  126. {
  127. for (L2ItemInstance item : _items)
  128. {
  129. if ((item != null) && (item.getId() == itemId) && !item.equals(itemToIgnore))
  130. {
  131. return item;
  132. }
  133. }
  134. return null;
  135. }
  136. /**
  137. * @param objectId the item object Id
  138. * @return item from inventory by objectId
  139. */
  140. public L2ItemInstance getItemByObjectId(int objectId)
  141. {
  142. for (L2ItemInstance item : _items)
  143. {
  144. if ((item != null) && (item.getObjectId() == objectId))
  145. {
  146. return item;
  147. }
  148. }
  149. return null;
  150. }
  151. /**
  152. * Gets the inventory item count by item Id and enchant level including equipped items.
  153. * @param itemId the item Id
  154. * @param enchantLevel the item enchant level, use -1 to match any enchant level
  155. * @return the inventory item count
  156. */
  157. public long getInventoryItemCount(int itemId, int enchantLevel)
  158. {
  159. return getInventoryItemCount(itemId, enchantLevel, true);
  160. }
  161. /**
  162. * Gets the inventory item count by item Id and enchant level, may include equipped items.
  163. * @param itemId the item Id
  164. * @param enchantLevel the item enchant level, use -1 to match any enchant level
  165. * @param includeEquipped if {@code true} includes equipped items in the result
  166. * @return the inventory item count
  167. */
  168. public long getInventoryItemCount(int itemId, int enchantLevel, boolean includeEquipped)
  169. {
  170. long count = 0;
  171. for (L2ItemInstance item : _items)
  172. {
  173. if ((item.getId() == itemId) && ((item.getEnchantLevel() == enchantLevel) || (enchantLevel < 0)) && (includeEquipped || !item.isEquipped()))
  174. {
  175. if (item.isStackable())
  176. {
  177. // FIXME: Zoey76: if there are more than one stacks of the same item Id
  178. // it will return the count of the last one, if is not possible to
  179. // have more than one stacks of the same item Id,
  180. // it will continue iterating over all items
  181. // possible fixes:
  182. // count += item.getCount();
  183. // or
  184. // count = item.getCount();
  185. // break;
  186. count = item.getCount();
  187. }
  188. else
  189. {
  190. count++;
  191. }
  192. }
  193. }
  194. return count;
  195. }
  196. /**
  197. * Adds item to inventory
  198. * @param process : String Identifier of process triggering this action
  199. * @param item : L2ItemInstance to be added
  200. * @param actor : L2PcInstance Player requesting the item add
  201. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  202. * @return L2ItemInstance corresponding to the new item or the updated item in inventory
  203. */
  204. public L2ItemInstance addItem(String process, L2ItemInstance item, L2PcInstance actor, Object reference)
  205. {
  206. L2ItemInstance olditem = getItemByItemId(item.getId());
  207. // If stackable item is found in inventory just add to current quantity
  208. if ((olditem != null) && olditem.isStackable())
  209. {
  210. long count = item.getCount();
  211. olditem.changeCount(process, count, actor, reference);
  212. olditem.setLastChange(L2ItemInstance.MODIFIED);
  213. // And destroys the item
  214. ItemTable.getInstance().destroyItem(process, item, actor, reference);
  215. item.updateDatabase();
  216. item = olditem;
  217. // Updates database
  218. float adenaRate = Config.RATE_DROP_AMOUNT_MULTIPLIER.containsKey(Inventory.ADENA_ID) ? Config.RATE_DROP_AMOUNT_MULTIPLIER.get(Inventory.ADENA_ID) : 1;
  219. if ((item.getId() == Inventory.ADENA_ID) && (count < (10000 * adenaRate)))
  220. {
  221. // Small adena changes won't be saved to database all the time
  222. if ((GameTimeController.getInstance().getGameTicks() % 5) == 0)
  223. {
  224. item.updateDatabase();
  225. }
  226. }
  227. else
  228. {
  229. item.updateDatabase();
  230. }
  231. }
  232. // If item hasn't be found in inventory, create new one
  233. else
  234. {
  235. item.setOwnerId(process, getOwnerId(), actor, reference);
  236. item.setItemLocation(getBaseLocation());
  237. item.setLastChange((L2ItemInstance.ADDED));
  238. // Add item in inventory
  239. addItem(item);
  240. // Updates database
  241. item.updateDatabase();
  242. }
  243. refreshWeight();
  244. return item;
  245. }
  246. /**
  247. * Adds item to inventory
  248. * @param process : String Identifier of process triggering this action
  249. * @param itemId : int Item Identifier of the item to be added
  250. * @param count : int Quantity of items to be added
  251. * @param actor : L2PcInstance Player requesting the item add
  252. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  253. * @return L2ItemInstance corresponding to the new item or the updated item in inventory
  254. */
  255. public L2ItemInstance addItem(String process, int itemId, long count, L2PcInstance actor, Object reference)
  256. {
  257. L2ItemInstance item = getItemByItemId(itemId);
  258. // If stackable item is found in inventory just add to current quantity
  259. if ((item != null) && item.isStackable())
  260. {
  261. item.changeCount(process, count, actor, reference);
  262. item.setLastChange(L2ItemInstance.MODIFIED);
  263. // Updates database
  264. // If Adena drop rate is not present it will be x1.
  265. float adenaRate = Config.RATE_DROP_AMOUNT_MULTIPLIER.containsKey(Inventory.ADENA_ID) ? Config.RATE_DROP_AMOUNT_MULTIPLIER.get(Inventory.ADENA_ID) : 1;
  266. if ((itemId == Inventory.ADENA_ID) && (count < (10000 * adenaRate)))
  267. {
  268. // Small adena changes won't be saved to database all the time
  269. if ((GameTimeController.getInstance().getGameTicks() % 5) == 0)
  270. {
  271. item.updateDatabase();
  272. }
  273. }
  274. else
  275. {
  276. item.updateDatabase();
  277. }
  278. }
  279. // If item hasn't be found in inventory, create new one
  280. else
  281. {
  282. for (int i = 0; i < count; i++)
  283. {
  284. L2Item template = ItemTable.getInstance().getTemplate(itemId);
  285. if (template == null)
  286. {
  287. _log.log(Level.WARNING, (actor != null ? "[" + actor.getName() + "] " : "") + "Invalid ItemId requested: ", itemId);
  288. return null;
  289. }
  290. item = ItemTable.getInstance().createItem(process, itemId, template.isStackable() ? count : 1, actor, reference);
  291. item.setOwnerId(getOwnerId());
  292. item.setItemLocation(getBaseLocation());
  293. item.setLastChange(L2ItemInstance.ADDED);
  294. // Add item in inventory
  295. addItem(item);
  296. // Updates database
  297. item.updateDatabase();
  298. // If stackable, end loop as entire count is included in 1 instance of item
  299. if (template.isStackable() || !Config.MULTIPLE_ITEM_DROP)
  300. {
  301. break;
  302. }
  303. }
  304. }
  305. refreshWeight();
  306. return item;
  307. }
  308. /**
  309. * Transfers item to another inventory
  310. * @param process string Identifier of process triggering this action
  311. * @param objectId Item Identifier of the item to be transfered
  312. * @param count Quantity of items to be transfered
  313. * @param target the item container where the item will be moved.
  314. * @param actor Player requesting the item transfer
  315. * @param reference Object Object referencing current action like NPC selling item or previous item in transformation
  316. * @return L2ItemInstance corresponding to the new item or the updated item in inventory
  317. */
  318. public L2ItemInstance transferItem(String process, int objectId, long count, ItemContainer target, L2PcInstance actor, Object reference)
  319. {
  320. if (target == null)
  321. {
  322. return null;
  323. }
  324. L2ItemInstance sourceitem = getItemByObjectId(objectId);
  325. if (sourceitem == null)
  326. {
  327. return null;
  328. }
  329. L2ItemInstance targetitem = sourceitem.isStackable() ? target.getItemByItemId(sourceitem.getId()) : null;
  330. synchronized (sourceitem)
  331. {
  332. // check if this item still present in this container
  333. if (getItemByObjectId(objectId) != sourceitem)
  334. {
  335. return null;
  336. }
  337. // Check if requested quantity is available
  338. if (count > sourceitem.getCount())
  339. {
  340. count = sourceitem.getCount();
  341. }
  342. // If possible, move entire item object
  343. if ((sourceitem.getCount() == count) && (targetitem == null))
  344. {
  345. removeItem(sourceitem);
  346. target.addItem(process, sourceitem, actor, reference);
  347. targetitem = sourceitem;
  348. }
  349. else
  350. {
  351. if (sourceitem.getCount() > count) // If possible, only update counts
  352. {
  353. sourceitem.changeCount(process, -count, actor, reference);
  354. }
  355. else
  356. // Otherwise destroy old item
  357. {
  358. removeItem(sourceitem);
  359. ItemTable.getInstance().destroyItem(process, sourceitem, actor, reference);
  360. }
  361. if (targetitem != null) // If possible, only update counts
  362. {
  363. targetitem.changeCount(process, count, actor, reference);
  364. }
  365. else
  366. // Otherwise add new item
  367. {
  368. targetitem = target.addItem(process, sourceitem.getId(), count, actor, reference);
  369. }
  370. }
  371. // Updates database
  372. sourceitem.updateDatabase(true);
  373. if ((targetitem != sourceitem) && (targetitem != null))
  374. {
  375. targetitem.updateDatabase();
  376. }
  377. if (sourceitem.isAugmented())
  378. {
  379. sourceitem.getAugmentation().removeBonus(actor);
  380. }
  381. refreshWeight();
  382. target.refreshWeight();
  383. }
  384. return targetitem;
  385. }
  386. /**
  387. * Destroy item from inventory and updates database
  388. * @param process : String Identifier of process triggering this action
  389. * @param item : L2ItemInstance to be destroyed
  390. * @param actor : L2PcInstance Player requesting the item destroy
  391. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  392. * @return L2ItemInstance corresponding to the destroyed item or the updated item in inventory
  393. */
  394. public L2ItemInstance destroyItem(String process, L2ItemInstance item, L2PcInstance actor, Object reference)
  395. {
  396. return this.destroyItem(process, item, item.getCount(), actor, reference);
  397. }
  398. /**
  399. * Destroy item from inventory and updates database
  400. * @param process : String Identifier of process triggering this action
  401. * @param item : L2ItemInstance to be destroyed
  402. * @param count
  403. * @param actor : L2PcInstance Player requesting the item destroy
  404. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  405. * @return L2ItemInstance corresponding to the destroyed item or the updated item in inventory
  406. */
  407. public L2ItemInstance destroyItem(String process, L2ItemInstance item, long count, L2PcInstance actor, Object reference)
  408. {
  409. synchronized (item)
  410. {
  411. // Adjust item quantity
  412. if (item.getCount() > count)
  413. {
  414. item.changeCount(process, -count, actor, reference);
  415. item.setLastChange(L2ItemInstance.MODIFIED);
  416. // don't update often for untraced items
  417. if ((process != null) || ((GameTimeController.getInstance().getGameTicks() % 10) == 0))
  418. {
  419. item.updateDatabase();
  420. }
  421. refreshWeight();
  422. }
  423. else
  424. {
  425. if (item.getCount() < count)
  426. {
  427. return null;
  428. }
  429. boolean removed = removeItem(item);
  430. if (!removed)
  431. {
  432. return null;
  433. }
  434. ItemTable.getInstance().destroyItem(process, item, actor, reference);
  435. item.updateDatabase();
  436. refreshWeight();
  437. }
  438. item.deleteMe();
  439. }
  440. return item;
  441. }
  442. /**
  443. * Destroy item from inventory by using its <B>objectID</B> and updates database
  444. * @param process : String Identifier of process triggering this action
  445. * @param objectId : int Item Instance identifier of the item to be destroyed
  446. * @param count : int Quantity of items to be destroyed
  447. * @param actor : L2PcInstance Player requesting the item destroy
  448. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  449. * @return L2ItemInstance corresponding to the destroyed item or the updated item in inventory
  450. */
  451. public L2ItemInstance destroyItem(String process, int objectId, long count, L2PcInstance actor, Object reference)
  452. {
  453. L2ItemInstance item = getItemByObjectId(objectId);
  454. if (item == null)
  455. {
  456. return null;
  457. }
  458. return this.destroyItem(process, item, count, actor, reference);
  459. }
  460. /**
  461. * Destroy item from inventory by using its <B>itemId</B> and updates database
  462. * @param process : String Identifier of process triggering this action
  463. * @param itemId : int Item identifier of the item to be destroyed
  464. * @param count : int Quantity of items to be destroyed
  465. * @param actor : L2PcInstance Player requesting the item destroy
  466. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  467. * @return L2ItemInstance corresponding to the destroyed item or the updated item in inventory
  468. */
  469. public L2ItemInstance destroyItemByItemId(String process, int itemId, long count, L2PcInstance actor, Object reference)
  470. {
  471. L2ItemInstance item = getItemByItemId(itemId);
  472. if (item == null)
  473. {
  474. return null;
  475. }
  476. return destroyItem(process, item, count, actor, reference);
  477. }
  478. /**
  479. * Destroy all items from inventory and updates database
  480. * @param process : String Identifier of process triggering this action
  481. * @param actor : L2PcInstance Player requesting the item destroy
  482. * @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
  483. */
  484. public void destroyAllItems(String process, L2PcInstance actor, Object reference)
  485. {
  486. for (L2ItemInstance item : _items)
  487. {
  488. if (item != null)
  489. {
  490. destroyItem(process, item, actor, reference);
  491. }
  492. }
  493. }
  494. /**
  495. * @return warehouse Adena.
  496. */
  497. public long getAdena()
  498. {
  499. long count = 0;
  500. for (L2ItemInstance item : _items)
  501. {
  502. if ((item != null) && (item.getId() == Inventory.ADENA_ID))
  503. {
  504. count = item.getCount();
  505. return count;
  506. }
  507. }
  508. return count;
  509. }
  510. /**
  511. * Adds item to inventory for further adjustments.
  512. * @param item : L2ItemInstance to be added from inventory
  513. */
  514. protected void addItem(L2ItemInstance item)
  515. {
  516. _items.add(item);
  517. }
  518. /**
  519. * Removes item from inventory for further adjustments.
  520. * @param item : L2ItemInstance to be removed from inventory
  521. * @return
  522. */
  523. protected boolean removeItem(L2ItemInstance item)
  524. {
  525. return _items.remove(item);
  526. }
  527. /**
  528. * Refresh the weight of equipment loaded
  529. */
  530. protected void refreshWeight()
  531. {
  532. }
  533. /**
  534. * Delete item object from world
  535. */
  536. public void deleteMe()
  537. {
  538. if (getOwner() != null)
  539. {
  540. for (L2ItemInstance item : _items)
  541. {
  542. if (item != null)
  543. {
  544. item.updateDatabase(true);
  545. item.deleteMe();
  546. L2World.getInstance().removeObject(item);
  547. }
  548. }
  549. }
  550. _items.clear();
  551. }
  552. /**
  553. * Update database with items in inventory
  554. */
  555. public void updateDatabase()
  556. {
  557. if (getOwner() != null)
  558. {
  559. for (L2ItemInstance item : _items)
  560. {
  561. if (item != null)
  562. {
  563. item.updateDatabase(true);
  564. }
  565. }
  566. }
  567. }
  568. /**
  569. * Get back items in container from database
  570. */
  571. public void restore()
  572. {
  573. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  574. PreparedStatement statement = con.prepareStatement("SELECT object_id, item_id, count, enchant_level, loc, loc_data, custom_type1, custom_type2, mana_left, time FROM items WHERE owner_id=? AND (loc=?)"))
  575. {
  576. statement.setInt(1, getOwnerId());
  577. statement.setString(2, getBaseLocation().name());
  578. try (ResultSet inv = statement.executeQuery())
  579. {
  580. L2ItemInstance item;
  581. while (inv.next())
  582. {
  583. item = L2ItemInstance.restoreFromDb(getOwnerId(), inv);
  584. if (item == null)
  585. {
  586. continue;
  587. }
  588. L2World.getInstance().storeObject(item);
  589. L2PcInstance owner = getOwner() == null ? null : getOwner().getActingPlayer();
  590. // If stackable item is found in inventory just add to current quantity
  591. if (item.isStackable() && (getItemByItemId(item.getId()) != null))
  592. {
  593. addItem("Restore", item, owner, null);
  594. }
  595. else
  596. {
  597. addItem(item);
  598. }
  599. }
  600. }
  601. refreshWeight();
  602. }
  603. catch (Exception e)
  604. {
  605. _log.log(Level.WARNING, "could not restore container:", e);
  606. }
  607. }
  608. public boolean validateCapacity(long slots)
  609. {
  610. return true;
  611. }
  612. public boolean validateWeight(long weight)
  613. {
  614. return true;
  615. }
  616. /**
  617. * If the item is stackable validates 1 slot, if the item isn't stackable validates the item count.
  618. * @param itemId the item Id to verify
  619. * @param count amount of item's weight to validate
  620. * @return {@code true} if the item doesn't exists or it validates its slot count
  621. */
  622. public boolean validateCapacityByItemId(int itemId, long count)
  623. {
  624. final L2Item template = ItemTable.getInstance().getTemplate(itemId);
  625. return (template == null) || (template.isStackable() ? validateCapacity(1) : validateCapacity(count));
  626. }
  627. /**
  628. * @param itemId the item Id to verify
  629. * @param count amount of item's weight to validate
  630. * @return {@code true} if the item doesn't exists or it validates its weight
  631. */
  632. public boolean validateWeightByItemId(int itemId, long count)
  633. {
  634. final L2Item template = ItemTable.getInstance().getTemplate(itemId);
  635. return (template == null) || validateWeight(template.getWeight() * count);
  636. }
  637. }