RecipeController.java 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026
  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;
  16. import java.io.File;
  17. import java.io.IOException;
  18. import java.util.Arrays;
  19. import java.util.List;
  20. import java.util.Map;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import javax.xml.parsers.DocumentBuilderFactory;
  24. import javax.xml.parsers.ParserConfigurationException;
  25. import javolution.util.FastList;
  26. import javolution.util.FastMap;
  27. import org.w3c.dom.Document;
  28. import org.w3c.dom.NamedNodeMap;
  29. import org.w3c.dom.Node;
  30. import org.xml.sax.SAXException;
  31. import com.l2jserver.Config;
  32. import com.l2jserver.gameserver.datatables.ItemTable;
  33. import com.l2jserver.gameserver.model.L2ItemInstance;
  34. import com.l2jserver.gameserver.model.L2ManufactureItem;
  35. import com.l2jserver.gameserver.model.L2RecipeInstance;
  36. import com.l2jserver.gameserver.model.L2RecipeList;
  37. import com.l2jserver.gameserver.model.L2RecipeStatInstance;
  38. import com.l2jserver.gameserver.model.L2Skill;
  39. import com.l2jserver.gameserver.model.TempItem;
  40. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  41. import com.l2jserver.gameserver.model.itemcontainer.Inventory;
  42. import com.l2jserver.gameserver.network.SystemMessageId;
  43. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  44. import com.l2jserver.gameserver.network.serverpackets.ItemList;
  45. import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
  46. import com.l2jserver.gameserver.network.serverpackets.RecipeBookItemList;
  47. import com.l2jserver.gameserver.network.serverpackets.RecipeItemMakeInfo;
  48. import com.l2jserver.gameserver.network.serverpackets.RecipeShopItemInfo;
  49. import com.l2jserver.gameserver.network.serverpackets.SetupGauge;
  50. import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
  51. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  52. import com.l2jserver.gameserver.skills.Stats;
  53. import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
  54. import com.l2jserver.gameserver.templates.StatsSet;
  55. import com.l2jserver.gameserver.templates.item.L2Item;
  56. import com.l2jserver.gameserver.util.Util;
  57. import com.l2jserver.util.Rnd;
  58. public class RecipeController
  59. {
  60. protected static final Logger _log = Logger.getLogger(RecipeController.class.getName());
  61. private Map<Integer, L2RecipeList> _lists;
  62. private static final Map<Integer, RecipeItemMaker> _activeMakers = new FastMap<Integer, RecipeItemMaker>();
  63. private static final String RECIPES_FILE = "recipes.xml";
  64. public static RecipeController getInstance()
  65. {
  66. return SingletonHolder._instance;
  67. }
  68. private RecipeController()
  69. {
  70. _lists = new FastMap<Integer, L2RecipeList>();
  71. try
  72. {
  73. loadFromXML();
  74. _log.info("RecipeController: Loaded " + _lists.size() + " recipes.");
  75. }
  76. catch (Exception e)
  77. {
  78. _log.log(Level.SEVERE, "Failed loading recipe list", e);
  79. }
  80. }
  81. public int getRecipesCount()
  82. {
  83. return _lists.size();
  84. }
  85. public L2RecipeList getRecipeList(int listId)
  86. {
  87. return _lists.get(listId);
  88. }
  89. public L2RecipeList getRecipeByItemId(int itemId)
  90. {
  91. for (L2RecipeList find : _lists.values())
  92. {
  93. if (find.getRecipeId() == itemId)
  94. {
  95. return find;
  96. }
  97. }
  98. return null;
  99. }
  100. public int[] getAllItemIds()
  101. {
  102. int[] idList = new int[_lists.size()];
  103. int i = 0;
  104. for (L2RecipeList rec : _lists.values())
  105. {
  106. idList[i++] = rec.getRecipeId();
  107. }
  108. return idList;
  109. }
  110. public synchronized void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft)
  111. {
  112. RecipeItemMaker maker = null;
  113. if (Config.ALT_GAME_CREATION)
  114. {
  115. maker = _activeMakers.get(player.getObjectId());
  116. }
  117. if (maker == null)
  118. {
  119. RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp());
  120. response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook());
  121. player.sendPacket(response);
  122. return;
  123. }
  124. player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING));
  125. }
  126. public synchronized void requestMakeItemAbort(L2PcInstance player)
  127. {
  128. _activeMakers.remove(player.getObjectId()); // TODO: anything else here?
  129. }
  130. public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance player)
  131. {
  132. L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
  133. if (recipeList == null)
  134. {
  135. return;
  136. }
  137. List<L2RecipeList> dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook());
  138. List<L2RecipeList> commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook());
  139. if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
  140. {
  141. Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
  142. return;
  143. }
  144. RecipeItemMaker maker;
  145. if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(manufacturer.getObjectId())) != null)) // check if busy
  146. {
  147. player.sendMessage("Manufacturer is busy, please try later.");
  148. return;
  149. }
  150. maker = new RecipeItemMaker(manufacturer, recipeList, player);
  151. if (maker._isValid)
  152. {
  153. if (Config.ALT_GAME_CREATION)
  154. {
  155. _activeMakers.put(manufacturer.getObjectId(), maker);
  156. ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
  157. }
  158. else
  159. {
  160. maker.run();
  161. }
  162. }
  163. }
  164. public synchronized void requestMakeItem(L2PcInstance player, int recipeListId)
  165. {
  166. if (AttackStanceTaskManager.getInstance().getAttackStanceTask(player) || player.isInDuel())
  167. {
  168. player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANT_OPERATE_PRIVATE_STORE_DURING_COMBAT));
  169. return;
  170. }
  171. L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
  172. if (recipeList == null)
  173. {
  174. return;
  175. }
  176. List<L2RecipeList> dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook());
  177. List<L2RecipeList> commonRecipes = Arrays.asList(player.getCommonRecipeBook());
  178. if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
  179. {
  180. Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
  181. return;
  182. }
  183. RecipeItemMaker maker;
  184. // check if already busy (possible in alt mode only)
  185. if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(player.getObjectId())) != null))
  186. {
  187. SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1);
  188. sm.addItemName(recipeList.getItemId());
  189. sm.addString("You are busy creating");
  190. player.sendPacket(sm);
  191. return;
  192. }
  193. maker = new RecipeItemMaker(player, recipeList, player);
  194. if (maker._isValid)
  195. {
  196. if (Config.ALT_GAME_CREATION)
  197. {
  198. _activeMakers.put(player.getObjectId(), maker);
  199. ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
  200. }
  201. else
  202. {
  203. maker.run();
  204. }
  205. }
  206. }
  207. private void loadFromXML() throws SAXException, IOException, ParserConfigurationException
  208. {
  209. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  210. factory.setValidating(false);
  211. factory.setIgnoringComments(true);
  212. File file = new File(Config.DATAPACK_ROOT + "/data/" + RECIPES_FILE);
  213. if (file.exists())
  214. {
  215. Document doc = factory.newDocumentBuilder().parse(file);
  216. List<L2RecipeInstance> recipePartList = new FastList<L2RecipeInstance>();
  217. List<L2RecipeStatInstance> recipeStatUseList = new FastList<L2RecipeStatInstance>();
  218. List<L2RecipeStatInstance> recipeAltStatChangeList = new FastList<L2RecipeStatInstance>();
  219. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  220. {
  221. if ("list".equalsIgnoreCase(n.getNodeName()))
  222. {
  223. recipesFile: for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  224. {
  225. if ("item".equalsIgnoreCase(d.getNodeName()))
  226. {
  227. recipePartList.clear();
  228. recipeStatUseList.clear();
  229. recipeAltStatChangeList.clear();
  230. NamedNodeMap attrs = d.getAttributes();
  231. Node att;
  232. int id = -1;
  233. boolean haveRare = false;
  234. StatsSet set = new StatsSet();
  235. att = attrs.getNamedItem("id");
  236. if (att == null)
  237. {
  238. _log.severe("Missing id for recipe item, skipping");
  239. continue;
  240. }
  241. id = Integer.parseInt(att.getNodeValue());
  242. set.set("id", id);
  243. att = attrs.getNamedItem("recipeId");
  244. if (att == null)
  245. {
  246. _log.severe("Missing recipeId for recipe item id: " + id + ", skipping");
  247. continue;
  248. }
  249. set.set("recipeId", Integer.parseInt(att.getNodeValue()));
  250. att = attrs.getNamedItem("name");
  251. if (att == null)
  252. {
  253. _log.severe("Missing name for recipe item id: " + id + ", skipping");
  254. continue;
  255. }
  256. set.set("recipeName", att.getNodeValue());
  257. att = attrs.getNamedItem("craftLevel");
  258. if (att == null)
  259. {
  260. _log.severe("Missing level for recipe item id: " + id + ", skipping");
  261. continue;
  262. }
  263. set.set("craftLevel", Integer.parseInt(att.getNodeValue()));
  264. att = attrs.getNamedItem("type");
  265. if (att == null)
  266. {
  267. _log.severe("Missing type for recipe item id: " + id + ", skipping");
  268. continue;
  269. }
  270. set.set("isDwarvenRecipe", att.getNodeValue().equalsIgnoreCase("dwarven"));
  271. att = attrs.getNamedItem("successRate");
  272. if (att == null)
  273. {
  274. _log.severe("Missing successRate for recipe item id: " + id + ", skipping");
  275. continue;
  276. }
  277. set.set("successRate", Integer.parseInt(att.getNodeValue()));
  278. for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
  279. {
  280. if ("statUse".equalsIgnoreCase(c.getNodeName()))
  281. {
  282. String statName = c.getAttributes().getNamedItem("name").getNodeValue();
  283. int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
  284. try
  285. {
  286. recipeStatUseList.add(new L2RecipeStatInstance(statName, value));
  287. }
  288. catch (Exception e)
  289. {
  290. _log.severe("Error in StatUse parameter for recipe item id: " + id + ", skipping");
  291. continue recipesFile;
  292. }
  293. }
  294. else if ("altStatChange".equalsIgnoreCase(c.getNodeName()))
  295. {
  296. String statName = c.getAttributes().getNamedItem("name").getNodeValue();
  297. int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
  298. try
  299. {
  300. recipeAltStatChangeList.add(new L2RecipeStatInstance(statName, value));
  301. }
  302. catch (Exception e)
  303. {
  304. _log.severe("Error in AltStatChange parameter for recipe item id: " + id + ", skipping");
  305. continue recipesFile;
  306. }
  307. }
  308. else if ("ingredient".equalsIgnoreCase(c.getNodeName()))
  309. {
  310. int ingId = Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue());
  311. int ingCount = Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue());
  312. recipePartList.add(new L2RecipeInstance(ingId, ingCount));
  313. }
  314. else if ("production".equalsIgnoreCase(c.getNodeName()))
  315. {
  316. set.set("itemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
  317. set.set("count", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
  318. }
  319. else if ("productionRare".equalsIgnoreCase(c.getNodeName()))
  320. {
  321. set.set("rareItemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
  322. set.set("rareCount", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
  323. set.set("rarity", Integer.parseInt(c.getAttributes().getNamedItem("rarity").getNodeValue()));
  324. haveRare = true;
  325. }
  326. }
  327. L2RecipeList recipeList = new L2RecipeList(set, haveRare);
  328. for (L2RecipeInstance recipePart : recipePartList)
  329. {
  330. recipeList.addRecipe(recipePart);
  331. }
  332. for (L2RecipeStatInstance recipeStatUse : recipeStatUseList)
  333. {
  334. recipeList.addStatUse(recipeStatUse);
  335. }
  336. for (L2RecipeStatInstance recipeAltStatChange : recipeAltStatChangeList)
  337. {
  338. recipeList.addAltStatChange(recipeAltStatChange);
  339. }
  340. _lists.put(id, recipeList);
  341. }
  342. }
  343. }
  344. }
  345. }
  346. else
  347. {
  348. _log.severe("Recipes file (" + file.getAbsolutePath() + ") doesnt exists.");
  349. }
  350. }
  351. private static class RecipeItemMaker implements Runnable
  352. {
  353. protected boolean _isValid;
  354. protected List<TempItem> _items = null;
  355. protected final L2RecipeList _recipeList;
  356. protected final L2PcInstance _player; // "crafter"
  357. protected final L2PcInstance _target; // "customer"
  358. protected final L2Skill _skill;
  359. protected final int _skillId;
  360. protected final int _skillLevel;
  361. protected int _creationPasses = 1;
  362. protected int _itemGrab;
  363. protected int _exp = -1;
  364. protected int _sp = -1;
  365. protected long _price;
  366. protected int _totalItems;
  367. protected int _delay;
  368. public RecipeItemMaker(L2PcInstance pPlayer, L2RecipeList pRecipeList, L2PcInstance pTarget)
  369. {
  370. _player = pPlayer;
  371. _target = pTarget;
  372. _recipeList = pRecipeList;
  373. _isValid = false;
  374. _skillId = _recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN : L2Skill.SKILL_CREATE_COMMON;
  375. _skillLevel = _player.getSkillLevel(_skillId);
  376. _skill = _player.getKnownSkill(_skillId);
  377. _player.isInCraftMode(true);
  378. if (_player.isAlikeDead())
  379. {
  380. _player.sendPacket(ActionFailed.STATIC_PACKET);
  381. abort();
  382. return;
  383. }
  384. if (_target.isAlikeDead())
  385. {
  386. _target.sendPacket(ActionFailed.STATIC_PACKET);
  387. abort();
  388. return;
  389. }
  390. if (_target.isProcessingTransaction())
  391. {
  392. _target.sendPacket(ActionFailed.STATIC_PACKET);
  393. abort();
  394. return;
  395. }
  396. if (_player.isProcessingTransaction())
  397. {
  398. _player.sendPacket(ActionFailed.STATIC_PACKET);
  399. abort();
  400. return;
  401. }
  402. // validate recipe list
  403. if (_recipeList.getRecipes().length == 0)
  404. {
  405. _player.sendPacket(ActionFailed.STATIC_PACKET);
  406. abort();
  407. return;
  408. }
  409. // validate skill level
  410. if (_recipeList.getLevel() > _skillLevel)
  411. {
  412. _player.sendPacket(ActionFailed.STATIC_PACKET);
  413. abort();
  414. return;
  415. }
  416. // check that customer can afford to pay for creation services
  417. if (_player != _target)
  418. {
  419. for (L2ManufactureItem temp : _player.getCreateList().getList())
  420. {
  421. if (temp.getRecipeId() == _recipeList.getId()) // find recipe for item we want manufactured
  422. {
  423. _price = temp.getCost();
  424. if (_target.getAdena() < _price) // check price
  425. {
  426. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA));
  427. abort();
  428. return;
  429. }
  430. break;
  431. }
  432. }
  433. }
  434. // make temporary items
  435. if ((_items = listItems(false)) == null)
  436. {
  437. abort();
  438. return;
  439. }
  440. for (TempItem i : _items)
  441. {
  442. _totalItems += i.getQuantity();
  443. }
  444. // initial statUse checks
  445. if (!calculateStatUse(false, false))
  446. {
  447. abort();
  448. return;
  449. }
  450. // initial AltStatChange checks
  451. if (Config.ALT_GAME_CREATION)
  452. {
  453. calculateAltStatChange();
  454. }
  455. updateMakeInfo(true);
  456. updateCurMp();
  457. updateCurLoad();
  458. _player.isInCraftMode(false);
  459. _isValid = true;
  460. }
  461. public void run()
  462. {
  463. if (!Config.IS_CRAFTING_ENABLED)
  464. {
  465. _target.sendMessage("Item creation is currently disabled.");
  466. abort();
  467. return;
  468. }
  469. if ((_player == null) || (_target == null))
  470. {
  471. _log.warning("player or target == null (disconnected?), aborting" + _target + _player);
  472. abort();
  473. return;
  474. }
  475. if (!_player.isOnline() || !_target.isOnline())
  476. {
  477. _log.warning("player or target is not online, aborting " + _target + _player);
  478. abort();
  479. return;
  480. }
  481. if (Config.ALT_GAME_CREATION && (_activeMakers.get(_player.getObjectId()) == null))
  482. {
  483. if (_target != _player)
  484. {
  485. _target.sendMessage("Manufacture aborted");
  486. _player.sendMessage("Manufacture aborted");
  487. }
  488. else
  489. {
  490. _player.sendMessage("Item creation aborted");
  491. }
  492. abort();
  493. return;
  494. }
  495. if (Config.ALT_GAME_CREATION && !_items.isEmpty())
  496. {
  497. if (!calculateStatUse(true, true))
  498. {
  499. return; // check stat use
  500. }
  501. updateCurMp(); // update craft window mp bar
  502. grabSomeItems(); // grab (equip) some more items with a nice msg to player
  503. // if still not empty, schedule another pass
  504. if (!_items.isEmpty())
  505. {
  506. // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumables rates
  507. _delay = (int) ((Config.ALT_GAME_CREATION_SPEED * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND) / Config.RATE_CONSUMABLE_COST) * GameTimeController.MILLIS_IN_TICK;
  508. // FIXME: please fix this packet to show crafting animation (somebody)
  509. MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0);
  510. _player.broadcastPacket(msk);
  511. _player.sendPacket(new SetupGauge(0, _delay));
  512. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  513. }
  514. else
  515. {
  516. // for alt mode, sleep delay msec before finishing
  517. _player.sendPacket(new SetupGauge(0, _delay));
  518. try
  519. {
  520. Thread.sleep(_delay);
  521. }
  522. catch (InterruptedException e)
  523. {
  524. }
  525. finally
  526. {
  527. finishCrafting();
  528. }
  529. }
  530. } // for old craft mode just finish
  531. else
  532. {
  533. finishCrafting();
  534. }
  535. }
  536. private void finishCrafting()
  537. {
  538. if (!Config.ALT_GAME_CREATION)
  539. {
  540. calculateStatUse(false, true);
  541. }
  542. // first take adena for manufacture
  543. if ((_target != _player) && (_price > 0)) // customer must pay for services
  544. {
  545. // attempt to pay for item
  546. L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player);
  547. if (adenatransfer == null)
  548. {
  549. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA));
  550. abort();
  551. return;
  552. }
  553. }
  554. if ((_items = listItems(true)) == null) // this line actually takes materials from inventory
  555. { // handle possible cheaters here
  556. // (they click craft then try to get rid of items in order to get free craft)
  557. }
  558. else if (Rnd.get(100) < _recipeList.getSuccessRate())
  559. {
  560. rewardPlayer(); // and immediately puts created item in its place
  561. updateMakeInfo(true);
  562. }
  563. else
  564. {
  565. if (_target != _player)
  566. {
  567. SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CREATION_OF_S2_FOR_C1_AT_S3_ADENA_FAILED);
  568. msg.addString(_target.getName());
  569. msg.addItemName(_recipeList.getItemId());
  570. msg.addItemNumber(_price);
  571. _player.sendPacket(msg);
  572. msg = SystemMessage.getSystemMessage(SystemMessageId.C1_FAILED_TO_CREATE_S2_FOR_S3_ADENA);
  573. msg.addString(_player.getName());
  574. msg.addItemName(_recipeList.getItemId());
  575. msg.addItemNumber(_price);
  576. _target.sendPacket(msg);
  577. }
  578. else
  579. {
  580. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MIXING_FAILED));
  581. }
  582. updateMakeInfo(false);
  583. }
  584. // update load and mana bar of craft window
  585. updateCurMp();
  586. updateCurLoad();
  587. _activeMakers.remove(_player.getObjectId());
  588. _player.isInCraftMode(false);
  589. _target.sendPacket(new ItemList(_target, false));
  590. }
  591. private void updateMakeInfo(boolean success)
  592. {
  593. if (_target == _player)
  594. {
  595. _target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success));
  596. }
  597. else
  598. {
  599. _target.sendPacket(new RecipeShopItemInfo(_player, _recipeList.getId()));
  600. }
  601. }
  602. private void updateCurLoad()
  603. {
  604. StatusUpdate su = new StatusUpdate(_target);
  605. su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad());
  606. _target.sendPacket(su);
  607. }
  608. private void updateCurMp()
  609. {
  610. StatusUpdate su = new StatusUpdate(_target);
  611. su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getCurrentMp());
  612. _target.sendPacket(su);
  613. }
  614. private void grabSomeItems()
  615. {
  616. int grabItems = _itemGrab;
  617. while ((grabItems > 0) && !_items.isEmpty())
  618. {
  619. TempItem item = _items.get(0);
  620. int count = item.getQuantity();
  621. if (count >= grabItems)
  622. {
  623. count = grabItems;
  624. }
  625. item.setQuantity(item.getQuantity() - count);
  626. if (item.getQuantity() <= 0)
  627. {
  628. _items.remove(0);
  629. }
  630. else
  631. {
  632. _items.set(0, item);
  633. }
  634. grabItems -= count;
  635. if (_target == _player)
  636. {
  637. SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S2_EQUIPPED); // you equipped ...
  638. sm.addItemNumber(count);
  639. sm.addItemName(item.getItemId());
  640. _player.sendPacket(sm);
  641. }
  642. else
  643. {
  644. _target.sendMessage("Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName());
  645. }
  646. }
  647. }
  648. // AltStatChange parameters make their effect here
  649. private void calculateAltStatChange()
  650. {
  651. _itemGrab = _skillLevel;
  652. for (L2RecipeStatInstance altStatChange : _recipeList.getAltStatChange())
  653. {
  654. if (altStatChange.getType() == L2RecipeStatInstance.StatType.XP)
  655. {
  656. _exp = altStatChange.getValue();
  657. }
  658. else if (altStatChange.getType() == L2RecipeStatInstance.StatType.SP)
  659. {
  660. _sp = altStatChange.getValue();
  661. }
  662. else if (altStatChange.getType() == L2RecipeStatInstance.StatType.GIM)
  663. {
  664. _itemGrab *= altStatChange.getValue();
  665. }
  666. }
  667. // determine number of creation passes needed
  668. _creationPasses = (_totalItems / _itemGrab) + ((_totalItems % _itemGrab) != 0 ? 1 : 0);
  669. if (_creationPasses < 1)
  670. {
  671. _creationPasses = 1;
  672. }
  673. }
  674. // StatUse
  675. private boolean calculateStatUse(boolean isWait, boolean isReduce)
  676. {
  677. boolean ret = true;
  678. for (L2RecipeStatInstance statUse : _recipeList.getStatUse())
  679. {
  680. double modifiedValue = statUse.getValue() / _creationPasses;
  681. if (statUse.getType() == L2RecipeStatInstance.StatType.HP)
  682. {
  683. // we do not want to kill the player, so its CurrentHP must be greater than the reduce value
  684. if (_player.getCurrentHp() <= modifiedValue)
  685. {
  686. // rest (wait for HP)
  687. if (Config.ALT_GAME_CREATION && isWait)
  688. {
  689. _player.sendPacket(new SetupGauge(0, _delay));
  690. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  691. }
  692. else
  693. // no rest - report no hp
  694. {
  695. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_HP));
  696. abort();
  697. }
  698. ret = false;
  699. }
  700. else if (isReduce)
  701. {
  702. _player.reduceCurrentHp(modifiedValue, _player, _skill);
  703. }
  704. }
  705. else if (statUse.getType() == L2RecipeStatInstance.StatType.MP)
  706. {
  707. if (_player.getCurrentMp() < modifiedValue)
  708. {
  709. // rest (wait for MP)
  710. if (Config.ALT_GAME_CREATION && isWait)
  711. {
  712. _player.sendPacket(new SetupGauge(0, _delay));
  713. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  714. }
  715. else
  716. // no rest - report no mana
  717. {
  718. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_MP));
  719. abort();
  720. }
  721. ret = false;
  722. }
  723. else if (isReduce)
  724. {
  725. _player.reduceCurrentMp(modifiedValue);
  726. }
  727. }
  728. else
  729. {
  730. // there is an unknown StatUse value
  731. _target.sendMessage("Recipe error!!!, please tell this to your GM.");
  732. ret = false;
  733. abort();
  734. }
  735. }
  736. return ret;
  737. }
  738. private List<TempItem> listItems(boolean remove)
  739. {
  740. L2RecipeInstance[] recipes = _recipeList.getRecipes();
  741. Inventory inv = _target.getInventory();
  742. List<TempItem> materials = new FastList<TempItem>();
  743. SystemMessage sm;
  744. for (L2RecipeInstance recipe : recipes)
  745. {
  746. int quantity = _recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity();
  747. if (quantity > 0)
  748. {
  749. L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
  750. long itemQuantityAmount = item == null ? 0 : item.getCount();
  751. // check materials
  752. if (itemQuantityAmount < quantity)
  753. {
  754. sm = SystemMessage.getSystemMessage(SystemMessageId.MISSING_S2_S1_TO_CREATE);
  755. sm.addItemName(recipe.getItemId());
  756. sm.addItemNumber(quantity - itemQuantityAmount);
  757. _target.sendPacket(sm);
  758. abort();
  759. return null;
  760. }
  761. // make new temporary object, just for counting purposes
  762. TempItem temp = new TempItem(item, quantity);
  763. materials.add(temp);
  764. }
  765. }
  766. if (remove)
  767. {
  768. for (TempItem tmp : materials)
  769. {
  770. inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player);
  771. if (tmp.getQuantity() > 1)
  772. {
  773. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
  774. sm.addItemName(tmp.getItemId());
  775. sm.addItemNumber(tmp.getQuantity());
  776. _target.sendPacket(sm);
  777. }
  778. else
  779. {
  780. sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
  781. sm.addItemName(tmp.getItemId());
  782. _target.sendPacket(sm);
  783. }
  784. }
  785. }
  786. return materials;
  787. }
  788. private void abort()
  789. {
  790. updateMakeInfo(false);
  791. _player.isInCraftMode(false);
  792. _activeMakers.remove(_player.getObjectId());
  793. }
  794. private void rewardPlayer()
  795. {
  796. int rareProdId = _recipeList.getRareItemId();
  797. int itemId = _recipeList.getItemId();
  798. int itemCount = _recipeList.getCount();
  799. L2Item template = ItemTable.getInstance().getTemplate(itemId);
  800. // check that the current recipe has a rare production or not
  801. if ((rareProdId != -1) && ((rareProdId == itemId) || Config.CRAFT_MASTERWORK))
  802. {
  803. if (Rnd.get(100) < _recipeList.getRarity())
  804. {
  805. itemId = rareProdId;
  806. itemCount = _recipeList.getRareCount();
  807. }
  808. }
  809. _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player);
  810. // inform customer of earned item
  811. SystemMessage sm = null;
  812. if (_target != _player)
  813. {
  814. // inform manufacturer of earned profit
  815. if (itemCount == 1)
  816. {
  817. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA);
  818. sm.addString(_target.getName());
  819. sm.addItemName(itemId);
  820. sm.addItemNumber(_price);
  821. _player.sendPacket(sm);
  822. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA);
  823. sm.addString(_player.getName());
  824. sm.addItemName(itemId);
  825. sm.addItemNumber(_price);
  826. _target.sendPacket(sm);
  827. }
  828. else
  829. {
  830. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA);
  831. sm.addString(_target.getName());
  832. sm.addNumber(itemCount);
  833. sm.addItemName(itemId);
  834. sm.addItemNumber(_price);
  835. _player.sendPacket(sm);
  836. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_S4_ADENA);
  837. sm.addString(_player.getName());
  838. sm.addNumber(itemCount);
  839. sm.addItemName(itemId);
  840. sm.addItemNumber(_price);
  841. _target.sendPacket(sm);
  842. }
  843. }
  844. if (itemCount > 1)
  845. {
  846. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  847. sm.addItemName(itemId);
  848. sm.addItemNumber(itemCount);
  849. _target.sendPacket(sm);
  850. }
  851. else
  852. {
  853. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  854. sm.addItemName(itemId);
  855. _target.sendPacket(sm);
  856. }
  857. if (Config.ALT_GAME_CREATION)
  858. {
  859. int recipeLevel = _recipeList.getLevel();
  860. if (_exp < 0)
  861. {
  862. _exp = template.getReferencePrice() * itemCount;
  863. _exp /= recipeLevel;
  864. }
  865. if (_sp < 0)
  866. {
  867. _sp = _exp / 10;
  868. }
  869. if (itemId == rareProdId)
  870. {
  871. _exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  872. _sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  873. }
  874. if (_exp < 0)
  875. {
  876. _exp = 0;
  877. }
  878. if (_sp < 0)
  879. {
  880. _sp = 0;
  881. }
  882. for (int i = _skillLevel; i > recipeLevel; i--)
  883. {
  884. _exp /= 4;
  885. _sp /= 4;
  886. }
  887. // Added multiplication of Creation speed with XP/SP gain
  888. // slower crafting -> more XP, faster crafting -> less XP
  889. // you can use ALT_GAME_CREATION_XP_RATE/SP to
  890. // modify XP/SP gained (default = 1)
  891. _player.addExpAndSp((int) _player.calcStat(Stats.EXPSP_RATE, _exp * Config.ALT_GAME_CREATION_XP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null), (int) _player.calcStat(Stats.EXPSP_RATE, _sp * Config.ALT_GAME_CREATION_SP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null));
  892. }
  893. updateMakeInfo(true); // success
  894. }
  895. }
  896. private L2RecipeList getValidRecipeList(L2PcInstance player, int id)
  897. {
  898. L2RecipeList recipeList = getRecipeList(id);
  899. if ((recipeList == null) || (recipeList.getRecipes().length == 0))
  900. {
  901. player.sendMessage("No recipe for: " + id);
  902. player.isInCraftMode(false);
  903. return null;
  904. }
  905. return recipeList;
  906. }
  907. @SuppressWarnings("synthetic-access")
  908. private static class SingletonHolder
  909. {
  910. protected static final RecipeController _instance = new RecipeController();
  911. }
  912. }