RecipeController.java 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  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. @Override
  462. public void run()
  463. {
  464. if (!Config.IS_CRAFTING_ENABLED)
  465. {
  466. _target.sendMessage("Item creation is currently disabled.");
  467. abort();
  468. return;
  469. }
  470. if ((_player == null) || (_target == null))
  471. {
  472. _log.warning("player or target == null (disconnected?), aborting" + _target + _player);
  473. abort();
  474. return;
  475. }
  476. if (!_player.isOnline() || !_target.isOnline())
  477. {
  478. _log.warning("player or target is not online, aborting " + _target + _player);
  479. abort();
  480. return;
  481. }
  482. if (Config.ALT_GAME_CREATION && (_activeMakers.get(_player.getObjectId()) == null))
  483. {
  484. if (_target != _player)
  485. {
  486. _target.sendMessage("Manufacture aborted");
  487. _player.sendMessage("Manufacture aborted");
  488. }
  489. else
  490. {
  491. _player.sendMessage("Item creation aborted");
  492. }
  493. abort();
  494. return;
  495. }
  496. if (Config.ALT_GAME_CREATION && !_items.isEmpty())
  497. {
  498. if (!calculateStatUse(true, true))
  499. {
  500. return; // check stat use
  501. }
  502. updateCurMp(); // update craft window mp bar
  503. grabSomeItems(); // grab (equip) some more items with a nice msg to player
  504. // if still not empty, schedule another pass
  505. if (!_items.isEmpty())
  506. {
  507. // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumables rates
  508. _delay = (int) ((Config.ALT_GAME_CREATION_SPEED * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND) / Config.RATE_CONSUMABLE_COST) * GameTimeController.MILLIS_IN_TICK;
  509. // FIXME: please fix this packet to show crafting animation (somebody)
  510. MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0);
  511. _player.broadcastPacket(msk);
  512. _player.sendPacket(new SetupGauge(0, _delay));
  513. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  514. }
  515. else
  516. {
  517. // for alt mode, sleep delay msec before finishing
  518. _player.sendPacket(new SetupGauge(0, _delay));
  519. try
  520. {
  521. Thread.sleep(_delay);
  522. }
  523. catch (InterruptedException e)
  524. {
  525. }
  526. finally
  527. {
  528. finishCrafting();
  529. }
  530. }
  531. } // for old craft mode just finish
  532. else
  533. {
  534. finishCrafting();
  535. }
  536. }
  537. private void finishCrafting()
  538. {
  539. if (!Config.ALT_GAME_CREATION)
  540. {
  541. calculateStatUse(false, true);
  542. }
  543. // first take adena for manufacture
  544. if ((_target != _player) && (_price > 0)) // customer must pay for services
  545. {
  546. // attempt to pay for item
  547. L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player);
  548. if (adenatransfer == null)
  549. {
  550. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA));
  551. abort();
  552. return;
  553. }
  554. }
  555. if ((_items = listItems(true)) == null) // this line actually takes materials from inventory
  556. { // handle possible cheaters here
  557. // (they click craft then try to get rid of items in order to get free craft)
  558. }
  559. else if (Rnd.get(100) < _recipeList.getSuccessRate())
  560. {
  561. rewardPlayer(); // and immediately puts created item in its place
  562. updateMakeInfo(true);
  563. }
  564. else
  565. {
  566. if (_target != _player)
  567. {
  568. SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CREATION_OF_S2_FOR_C1_AT_S3_ADENA_FAILED);
  569. msg.addString(_target.getName());
  570. msg.addItemName(_recipeList.getItemId());
  571. msg.addItemNumber(_price);
  572. _player.sendPacket(msg);
  573. msg = SystemMessage.getSystemMessage(SystemMessageId.C1_FAILED_TO_CREATE_S2_FOR_S3_ADENA);
  574. msg.addString(_player.getName());
  575. msg.addItemName(_recipeList.getItemId());
  576. msg.addItemNumber(_price);
  577. _target.sendPacket(msg);
  578. }
  579. else
  580. {
  581. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ITEM_MIXING_FAILED));
  582. }
  583. updateMakeInfo(false);
  584. }
  585. // update load and mana bar of craft window
  586. updateCurMp();
  587. updateCurLoad();
  588. _activeMakers.remove(_player.getObjectId());
  589. _player.isInCraftMode(false);
  590. _target.sendPacket(new ItemList(_target, false));
  591. }
  592. private void updateMakeInfo(boolean success)
  593. {
  594. if (_target == _player)
  595. {
  596. _target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success));
  597. }
  598. else
  599. {
  600. _target.sendPacket(new RecipeShopItemInfo(_player, _recipeList.getId()));
  601. }
  602. }
  603. private void updateCurLoad()
  604. {
  605. StatusUpdate su = new StatusUpdate(_target);
  606. su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad());
  607. _target.sendPacket(su);
  608. }
  609. private void updateCurMp()
  610. {
  611. StatusUpdate su = new StatusUpdate(_target);
  612. su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getCurrentMp());
  613. _target.sendPacket(su);
  614. }
  615. private void grabSomeItems()
  616. {
  617. int grabItems = _itemGrab;
  618. while ((grabItems > 0) && !_items.isEmpty())
  619. {
  620. TempItem item = _items.get(0);
  621. int count = item.getQuantity();
  622. if (count >= grabItems)
  623. {
  624. count = grabItems;
  625. }
  626. item.setQuantity(item.getQuantity() - count);
  627. if (item.getQuantity() <= 0)
  628. {
  629. _items.remove(0);
  630. }
  631. else
  632. {
  633. _items.set(0, item);
  634. }
  635. grabItems -= count;
  636. if (_target == _player)
  637. {
  638. SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S2_EQUIPPED); // you equipped ...
  639. sm.addItemNumber(count);
  640. sm.addItemName(item.getItemId());
  641. _player.sendPacket(sm);
  642. }
  643. else
  644. {
  645. _target.sendMessage("Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName());
  646. }
  647. }
  648. }
  649. // AltStatChange parameters make their effect here
  650. private void calculateAltStatChange()
  651. {
  652. _itemGrab = _skillLevel;
  653. for (L2RecipeStatInstance altStatChange : _recipeList.getAltStatChange())
  654. {
  655. if (altStatChange.getType() == L2RecipeStatInstance.StatType.XP)
  656. {
  657. _exp = altStatChange.getValue();
  658. }
  659. else if (altStatChange.getType() == L2RecipeStatInstance.StatType.SP)
  660. {
  661. _sp = altStatChange.getValue();
  662. }
  663. else if (altStatChange.getType() == L2RecipeStatInstance.StatType.GIM)
  664. {
  665. _itemGrab *= altStatChange.getValue();
  666. }
  667. }
  668. // determine number of creation passes needed
  669. _creationPasses = (_totalItems / _itemGrab) + ((_totalItems % _itemGrab) != 0 ? 1 : 0);
  670. if (_creationPasses < 1)
  671. {
  672. _creationPasses = 1;
  673. }
  674. }
  675. // StatUse
  676. private boolean calculateStatUse(boolean isWait, boolean isReduce)
  677. {
  678. boolean ret = true;
  679. for (L2RecipeStatInstance statUse : _recipeList.getStatUse())
  680. {
  681. double modifiedValue = statUse.getValue() / _creationPasses;
  682. if (statUse.getType() == L2RecipeStatInstance.StatType.HP)
  683. {
  684. // we do not want to kill the player, so its CurrentHP must be greater than the reduce value
  685. if (_player.getCurrentHp() <= modifiedValue)
  686. {
  687. // rest (wait for HP)
  688. if (Config.ALT_GAME_CREATION && isWait)
  689. {
  690. _player.sendPacket(new SetupGauge(0, _delay));
  691. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  692. }
  693. else
  694. // no rest - report no hp
  695. {
  696. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_HP));
  697. abort();
  698. }
  699. ret = false;
  700. }
  701. else if (isReduce)
  702. {
  703. _player.reduceCurrentHp(modifiedValue, _player, _skill);
  704. }
  705. }
  706. else if (statUse.getType() == L2RecipeStatInstance.StatType.MP)
  707. {
  708. if (_player.getCurrentMp() < modifiedValue)
  709. {
  710. // rest (wait for MP)
  711. if (Config.ALT_GAME_CREATION && isWait)
  712. {
  713. _player.sendPacket(new SetupGauge(0, _delay));
  714. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  715. }
  716. else
  717. // no rest - report no mana
  718. {
  719. _target.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.NOT_ENOUGH_MP));
  720. abort();
  721. }
  722. ret = false;
  723. }
  724. else if (isReduce)
  725. {
  726. _player.reduceCurrentMp(modifiedValue);
  727. }
  728. }
  729. else
  730. {
  731. // there is an unknown StatUse value
  732. _target.sendMessage("Recipe error!!!, please tell this to your GM.");
  733. ret = false;
  734. abort();
  735. }
  736. }
  737. return ret;
  738. }
  739. private List<TempItem> listItems(boolean remove)
  740. {
  741. L2RecipeInstance[] recipes = _recipeList.getRecipes();
  742. Inventory inv = _target.getInventory();
  743. List<TempItem> materials = new FastList<TempItem>();
  744. SystemMessage sm;
  745. for (L2RecipeInstance recipe : recipes)
  746. {
  747. int quantity = _recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity();
  748. if (quantity > 0)
  749. {
  750. L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
  751. long itemQuantityAmount = item == null ? 0 : item.getCount();
  752. // check materials
  753. if (itemQuantityAmount < quantity)
  754. {
  755. sm = SystemMessage.getSystemMessage(SystemMessageId.MISSING_S2_S1_TO_CREATE);
  756. sm.addItemName(recipe.getItemId());
  757. sm.addItemNumber(quantity - itemQuantityAmount);
  758. _target.sendPacket(sm);
  759. abort();
  760. return null;
  761. }
  762. // make new temporary object, just for counting purposes
  763. TempItem temp = new TempItem(item, quantity);
  764. materials.add(temp);
  765. }
  766. }
  767. if (remove)
  768. {
  769. for (TempItem tmp : materials)
  770. {
  771. inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player);
  772. if (tmp.getQuantity() > 1)
  773. {
  774. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
  775. sm.addItemName(tmp.getItemId());
  776. sm.addItemNumber(tmp.getQuantity());
  777. _target.sendPacket(sm);
  778. }
  779. else
  780. {
  781. sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
  782. sm.addItemName(tmp.getItemId());
  783. _target.sendPacket(sm);
  784. }
  785. }
  786. }
  787. return materials;
  788. }
  789. private void abort()
  790. {
  791. updateMakeInfo(false);
  792. _player.isInCraftMode(false);
  793. _activeMakers.remove(_player.getObjectId());
  794. }
  795. private void rewardPlayer()
  796. {
  797. int rareProdId = _recipeList.getRareItemId();
  798. int itemId = _recipeList.getItemId();
  799. int itemCount = _recipeList.getCount();
  800. L2Item template = ItemTable.getInstance().getTemplate(itemId);
  801. // check that the current recipe has a rare production or not
  802. if ((rareProdId != -1) && ((rareProdId == itemId) || Config.CRAFT_MASTERWORK))
  803. {
  804. if (Rnd.get(100) < _recipeList.getRarity())
  805. {
  806. itemId = rareProdId;
  807. itemCount = _recipeList.getRareCount();
  808. }
  809. }
  810. _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player);
  811. // inform customer of earned item
  812. SystemMessage sm = null;
  813. if (_target != _player)
  814. {
  815. // inform manufacturer of earned profit
  816. if (itemCount == 1)
  817. {
  818. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA);
  819. sm.addString(_target.getName());
  820. sm.addItemName(itemId);
  821. sm.addItemNumber(_price);
  822. _player.sendPacket(sm);
  823. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA);
  824. sm.addString(_player.getName());
  825. sm.addItemName(itemId);
  826. sm.addItemNumber(_price);
  827. _target.sendPacket(sm);
  828. }
  829. else
  830. {
  831. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA);
  832. sm.addString(_target.getName());
  833. sm.addNumber(itemCount);
  834. sm.addItemName(itemId);
  835. sm.addItemNumber(_price);
  836. _player.sendPacket(sm);
  837. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_S4_ADENA);
  838. sm.addString(_player.getName());
  839. sm.addNumber(itemCount);
  840. sm.addItemName(itemId);
  841. sm.addItemNumber(_price);
  842. _target.sendPacket(sm);
  843. }
  844. }
  845. if (itemCount > 1)
  846. {
  847. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  848. sm.addItemName(itemId);
  849. sm.addItemNumber(itemCount);
  850. _target.sendPacket(sm);
  851. }
  852. else
  853. {
  854. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  855. sm.addItemName(itemId);
  856. _target.sendPacket(sm);
  857. }
  858. if (Config.ALT_GAME_CREATION)
  859. {
  860. int recipeLevel = _recipeList.getLevel();
  861. if (_exp < 0)
  862. {
  863. _exp = template.getReferencePrice() * itemCount;
  864. _exp /= recipeLevel;
  865. }
  866. if (_sp < 0)
  867. {
  868. _sp = _exp / 10;
  869. }
  870. if (itemId == rareProdId)
  871. {
  872. _exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  873. _sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  874. }
  875. if (_exp < 0)
  876. {
  877. _exp = 0;
  878. }
  879. if (_sp < 0)
  880. {
  881. _sp = 0;
  882. }
  883. for (int i = _skillLevel; i > recipeLevel; i--)
  884. {
  885. _exp /= 4;
  886. _sp /= 4;
  887. }
  888. // Added multiplication of Creation speed with XP/SP gain
  889. // slower crafting -> more XP, faster crafting -> less XP
  890. // you can use ALT_GAME_CREATION_XP_RATE/SP to
  891. // modify XP/SP gained (default = 1)
  892. _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));
  893. }
  894. updateMakeInfo(true); // success
  895. }
  896. }
  897. private L2RecipeList getValidRecipeList(L2PcInstance player, int id)
  898. {
  899. L2RecipeList recipeList = getRecipeList(id);
  900. if ((recipeList == null) || (recipeList.getRecipes().length == 0))
  901. {
  902. player.sendMessage("No recipe for: " + id);
  903. player.isInCraftMode(false);
  904. return null;
  905. }
  906. return recipeList;
  907. }
  908. @SuppressWarnings("synthetic-access")
  909. private static class SingletonHolder
  910. {
  911. protected static final RecipeController _instance = new RecipeController();
  912. }
  913. }