ItemContainer.java 20 KB

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