2
0

RecipeController.java 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022
  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.L2ManufactureItem;
  34. import com.l2jserver.gameserver.model.L2RecipeInstance;
  35. import com.l2jserver.gameserver.model.L2RecipeList;
  36. import com.l2jserver.gameserver.model.L2RecipeStatInstance;
  37. import com.l2jserver.gameserver.model.StatsSet;
  38. import com.l2jserver.gameserver.model.TempItem;
  39. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  40. import com.l2jserver.gameserver.model.itemcontainer.Inventory;
  41. import com.l2jserver.gameserver.model.items.L2Item;
  42. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  43. import com.l2jserver.gameserver.model.skills.L2Skill;
  44. import com.l2jserver.gameserver.model.stats.Stats;
  45. import com.l2jserver.gameserver.network.SystemMessageId;
  46. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  47. import com.l2jserver.gameserver.network.serverpackets.ItemList;
  48. import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
  49. import com.l2jserver.gameserver.network.serverpackets.RecipeBookItemList;
  50. import com.l2jserver.gameserver.network.serverpackets.RecipeItemMakeInfo;
  51. import com.l2jserver.gameserver.network.serverpackets.RecipeShopItemInfo;
  52. import com.l2jserver.gameserver.network.serverpackets.SetupGauge;
  53. import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
  54. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  55. import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
  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 static final Map<Integer, L2RecipeList> _lists = new FastMap<Integer, L2RecipeList>();
  62. protected static final Map<Integer, RecipeItemMaker> _activeMakers = new FastMap<Integer, RecipeItemMaker>().shared();
  63. private static final String RECIPES_FILE = "recipes.xml";
  64. public static RecipeController getInstance()
  65. {
  66. return SingletonHolder._instance;
  67. }
  68. protected RecipeController()
  69. {
  70. try
  71. {
  72. loadFromXML();
  73. _log.info("RecipeController: Loaded " + _lists.size() + " recipes.");
  74. }
  75. catch (Exception e)
  76. {
  77. _log.log(Level.SEVERE, "Failed loading recipe list", e);
  78. }
  79. }
  80. public int getRecipesCount()
  81. {
  82. return _lists.size();
  83. }
  84. public L2RecipeList getRecipeList(int listId)
  85. {
  86. return _lists.get(listId);
  87. }
  88. public L2RecipeList getRecipeByItemId(int itemId)
  89. {
  90. for (L2RecipeList find : _lists.values())
  91. {
  92. if (find.getRecipeId() == itemId)
  93. {
  94. return find;
  95. }
  96. }
  97. return null;
  98. }
  99. public int[] getAllItemIds()
  100. {
  101. int[] idList = new int[_lists.size()];
  102. int i = 0;
  103. for (L2RecipeList rec : _lists.values())
  104. {
  105. idList[i++] = rec.getRecipeId();
  106. }
  107. return idList;
  108. }
  109. public synchronized void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft)
  110. {
  111. RecipeItemMaker maker = null;
  112. if (Config.ALT_GAME_CREATION)
  113. {
  114. maker = _activeMakers.get(player.getObjectId());
  115. }
  116. if (maker == null)
  117. {
  118. RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp());
  119. response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook());
  120. player.sendPacket(response);
  121. return;
  122. }
  123. player.sendPacket(SystemMessageId.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING);
  124. }
  125. public synchronized void requestMakeItemAbort(L2PcInstance player)
  126. {
  127. _activeMakers.remove(player.getObjectId()); // TODO: anything else here?
  128. }
  129. public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance player)
  130. {
  131. L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
  132. if (recipeList == null)
  133. {
  134. return;
  135. }
  136. List<L2RecipeList> dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook());
  137. List<L2RecipeList> commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook());
  138. if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
  139. {
  140. Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
  141. return;
  142. }
  143. RecipeItemMaker maker;
  144. if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(manufacturer.getObjectId())) != null)) // check if busy
  145. {
  146. player.sendMessage("Manufacturer is busy, please try later.");
  147. return;
  148. }
  149. maker = new RecipeItemMaker(manufacturer, recipeList, player);
  150. if (maker._isValid)
  151. {
  152. if (Config.ALT_GAME_CREATION)
  153. {
  154. _activeMakers.put(manufacturer.getObjectId(), maker);
  155. ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
  156. }
  157. else
  158. {
  159. maker.run();
  160. }
  161. }
  162. }
  163. public synchronized void requestMakeItem(L2PcInstance player, int recipeListId)
  164. {
  165. if (AttackStanceTaskManager.getInstance().getAttackStanceTask(player) || player.isInDuel())
  166. {
  167. player.sendPacket(SystemMessageId.CANT_OPERATE_PRIVATE_STORE_DURING_COMBAT);
  168. return;
  169. }
  170. L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
  171. if (recipeList == null)
  172. {
  173. return;
  174. }
  175. List<L2RecipeList> dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook());
  176. List<L2RecipeList> commonRecipes = Arrays.asList(player.getCommonRecipeBook());
  177. if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
  178. {
  179. Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
  180. return;
  181. }
  182. RecipeItemMaker maker;
  183. // check if already busy (possible in alt mode only)
  184. if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(player.getObjectId())) != null))
  185. {
  186. SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1);
  187. sm.addItemName(recipeList.getItemId());
  188. sm.addString("You are busy creating");
  189. player.sendPacket(sm);
  190. return;
  191. }
  192. maker = new RecipeItemMaker(player, recipeList, player);
  193. if (maker._isValid)
  194. {
  195. if (Config.ALT_GAME_CREATION)
  196. {
  197. _activeMakers.put(player.getObjectId(), maker);
  198. ThreadPoolManager.getInstance().scheduleGeneral(maker, 100);
  199. }
  200. else
  201. {
  202. maker.run();
  203. }
  204. }
  205. }
  206. private void loadFromXML() throws SAXException, IOException, ParserConfigurationException
  207. {
  208. DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
  209. factory.setValidating(false);
  210. factory.setIgnoringComments(true);
  211. File file = new File(Config.DATAPACK_ROOT + "/data/" + RECIPES_FILE);
  212. if (file.exists())
  213. {
  214. Document doc = factory.newDocumentBuilder().parse(file);
  215. List<L2RecipeInstance> recipePartList = new FastList<L2RecipeInstance>();
  216. List<L2RecipeStatInstance> recipeStatUseList = new FastList<L2RecipeStatInstance>();
  217. List<L2RecipeStatInstance> recipeAltStatChangeList = new FastList<L2RecipeStatInstance>();
  218. for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
  219. {
  220. if ("list".equalsIgnoreCase(n.getNodeName()))
  221. {
  222. recipesFile: for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  223. {
  224. if ("item".equalsIgnoreCase(d.getNodeName()))
  225. {
  226. recipePartList.clear();
  227. recipeStatUseList.clear();
  228. recipeAltStatChangeList.clear();
  229. NamedNodeMap attrs = d.getAttributes();
  230. Node att;
  231. int id = -1;
  232. boolean haveRare = false;
  233. StatsSet set = new StatsSet();
  234. att = attrs.getNamedItem("id");
  235. if (att == null)
  236. {
  237. _log.severe("Missing id for recipe item, skipping");
  238. continue;
  239. }
  240. id = Integer.parseInt(att.getNodeValue());
  241. set.set("id", id);
  242. att = attrs.getNamedItem("recipeId");
  243. if (att == null)
  244. {
  245. _log.severe("Missing recipeId for recipe item id: " + id + ", skipping");
  246. continue;
  247. }
  248. set.set("recipeId", Integer.parseInt(att.getNodeValue()));
  249. att = attrs.getNamedItem("name");
  250. if (att == null)
  251. {
  252. _log.severe("Missing name for recipe item id: " + id + ", skipping");
  253. continue;
  254. }
  255. set.set("recipeName", att.getNodeValue());
  256. att = attrs.getNamedItem("craftLevel");
  257. if (att == null)
  258. {
  259. _log.severe("Missing level for recipe item id: " + id + ", skipping");
  260. continue;
  261. }
  262. set.set("craftLevel", Integer.parseInt(att.getNodeValue()));
  263. att = attrs.getNamedItem("type");
  264. if (att == null)
  265. {
  266. _log.severe("Missing type for recipe item id: " + id + ", skipping");
  267. continue;
  268. }
  269. set.set("isDwarvenRecipe", att.getNodeValue().equalsIgnoreCase("dwarven"));
  270. att = attrs.getNamedItem("successRate");
  271. if (att == null)
  272. {
  273. _log.severe("Missing successRate for recipe item id: " + id + ", skipping");
  274. continue;
  275. }
  276. set.set("successRate", Integer.parseInt(att.getNodeValue()));
  277. for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
  278. {
  279. if ("statUse".equalsIgnoreCase(c.getNodeName()))
  280. {
  281. String statName = c.getAttributes().getNamedItem("name").getNodeValue();
  282. int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
  283. try
  284. {
  285. recipeStatUseList.add(new L2RecipeStatInstance(statName, value));
  286. }
  287. catch (Exception e)
  288. {
  289. _log.severe("Error in StatUse parameter for recipe item id: " + id + ", skipping");
  290. continue recipesFile;
  291. }
  292. }
  293. else if ("altStatChange".equalsIgnoreCase(c.getNodeName()))
  294. {
  295. String statName = c.getAttributes().getNamedItem("name").getNodeValue();
  296. int value = Integer.parseInt(c.getAttributes().getNamedItem("value").getNodeValue());
  297. try
  298. {
  299. recipeAltStatChangeList.add(new L2RecipeStatInstance(statName, value));
  300. }
  301. catch (Exception e)
  302. {
  303. _log.severe("Error in AltStatChange parameter for recipe item id: " + id + ", skipping");
  304. continue recipesFile;
  305. }
  306. }
  307. else if ("ingredient".equalsIgnoreCase(c.getNodeName()))
  308. {
  309. int ingId = Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue());
  310. int ingCount = Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue());
  311. recipePartList.add(new L2RecipeInstance(ingId, ingCount));
  312. }
  313. else if ("production".equalsIgnoreCase(c.getNodeName()))
  314. {
  315. set.set("itemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
  316. set.set("count", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
  317. }
  318. else if ("productionRare".equalsIgnoreCase(c.getNodeName()))
  319. {
  320. set.set("rareItemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue()));
  321. set.set("rareCount", Integer.parseInt(c.getAttributes().getNamedItem("count").getNodeValue()));
  322. set.set("rarity", Integer.parseInt(c.getAttributes().getNamedItem("rarity").getNodeValue()));
  323. haveRare = true;
  324. }
  325. }
  326. L2RecipeList recipeList = new L2RecipeList(set, haveRare);
  327. for (L2RecipeInstance recipePart : recipePartList)
  328. {
  329. recipeList.addRecipe(recipePart);
  330. }
  331. for (L2RecipeStatInstance recipeStatUse : recipeStatUseList)
  332. {
  333. recipeList.addStatUse(recipeStatUse);
  334. }
  335. for (L2RecipeStatInstance recipeAltStatChange : recipeAltStatChangeList)
  336. {
  337. recipeList.addAltStatChange(recipeAltStatChange);
  338. }
  339. _lists.put(id, recipeList);
  340. }
  341. }
  342. }
  343. }
  344. }
  345. else
  346. {
  347. _log.severe("Recipes file (" + file.getAbsolutePath() + ") doesnt exists.");
  348. }
  349. }
  350. private static class RecipeItemMaker implements Runnable
  351. {
  352. protected boolean _isValid;
  353. protected List<TempItem> _items = null;
  354. protected final L2RecipeList _recipeList;
  355. protected final L2PcInstance _player; // "crafter"
  356. protected final L2PcInstance _target; // "customer"
  357. protected final L2Skill _skill;
  358. protected final int _skillId;
  359. protected final int _skillLevel;
  360. protected int _creationPasses = 1;
  361. protected int _itemGrab;
  362. protected int _exp = -1;
  363. protected int _sp = -1;
  364. protected long _price;
  365. protected int _totalItems;
  366. protected int _delay;
  367. public RecipeItemMaker(L2PcInstance pPlayer, L2RecipeList pRecipeList, L2PcInstance pTarget)
  368. {
  369. _player = pPlayer;
  370. _target = pTarget;
  371. _recipeList = pRecipeList;
  372. _isValid = false;
  373. _skillId = _recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN : L2Skill.SKILL_CREATE_COMMON;
  374. _skillLevel = _player.getSkillLevel(_skillId);
  375. _skill = _player.getKnownSkill(_skillId);
  376. _player.isInCraftMode(true);
  377. if (_player.isAlikeDead())
  378. {
  379. _player.sendPacket(ActionFailed.STATIC_PACKET);
  380. abort();
  381. return;
  382. }
  383. if (_target.isAlikeDead())
  384. {
  385. _target.sendPacket(ActionFailed.STATIC_PACKET);
  386. abort();
  387. return;
  388. }
  389. if (_target.isProcessingTransaction())
  390. {
  391. _target.sendPacket(ActionFailed.STATIC_PACKET);
  392. abort();
  393. return;
  394. }
  395. if (_player.isProcessingTransaction())
  396. {
  397. _player.sendPacket(ActionFailed.STATIC_PACKET);
  398. abort();
  399. return;
  400. }
  401. // validate recipe list
  402. if (_recipeList.getRecipes().length == 0)
  403. {
  404. _player.sendPacket(ActionFailed.STATIC_PACKET);
  405. abort();
  406. return;
  407. }
  408. // validate skill level
  409. if (_recipeList.getLevel() > _skillLevel)
  410. {
  411. _player.sendPacket(ActionFailed.STATIC_PACKET);
  412. abort();
  413. return;
  414. }
  415. // check that customer can afford to pay for creation services
  416. if (_player != _target)
  417. {
  418. for (L2ManufactureItem temp : _player.getCreateList().getList())
  419. {
  420. if (temp.getRecipeId() == _recipeList.getId()) // find recipe for item we want manufactured
  421. {
  422. _price = temp.getCost();
  423. if (_target.getAdena() < _price) // check price
  424. {
  425. _target.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
  426. abort();
  427. return;
  428. }
  429. break;
  430. }
  431. }
  432. }
  433. // make temporary items
  434. if ((_items = listItems(false)) == null)
  435. {
  436. abort();
  437. return;
  438. }
  439. for (TempItem i : _items)
  440. {
  441. _totalItems += i.getQuantity();
  442. }
  443. // initial statUse checks
  444. if (!calculateStatUse(false, false))
  445. {
  446. abort();
  447. return;
  448. }
  449. // initial AltStatChange checks
  450. if (Config.ALT_GAME_CREATION)
  451. {
  452. calculateAltStatChange();
  453. }
  454. updateMakeInfo(true);
  455. updateCurMp();
  456. updateCurLoad();
  457. _player.isInCraftMode(false);
  458. _isValid = true;
  459. }
  460. @Override
  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(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(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. {
  694. _target.sendPacket(SystemMessageId.NOT_ENOUGH_HP);
  695. abort();
  696. }
  697. ret = false;
  698. }
  699. else if (isReduce)
  700. {
  701. _player.reduceCurrentHp(modifiedValue, _player, _skill);
  702. }
  703. }
  704. else if (statUse.getType() == L2RecipeStatInstance.StatType.MP)
  705. {
  706. if (_player.getCurrentMp() < modifiedValue)
  707. {
  708. // rest (wait for MP)
  709. if (Config.ALT_GAME_CREATION && isWait)
  710. {
  711. _player.sendPacket(new SetupGauge(0, _delay));
  712. ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay);
  713. }
  714. else
  715. {
  716. _target.sendPacket(SystemMessageId.NOT_ENOUGH_MP);
  717. abort();
  718. }
  719. ret = false;
  720. }
  721. else if (isReduce)
  722. {
  723. _player.reduceCurrentMp(modifiedValue);
  724. }
  725. }
  726. else
  727. {
  728. // there is an unknown StatUse value
  729. _target.sendMessage("Recipe error!!!, please tell this to your GM.");
  730. ret = false;
  731. abort();
  732. }
  733. }
  734. return ret;
  735. }
  736. private List<TempItem> listItems(boolean remove)
  737. {
  738. L2RecipeInstance[] recipes = _recipeList.getRecipes();
  739. Inventory inv = _target.getInventory();
  740. List<TempItem> materials = new FastList<TempItem>();
  741. SystemMessage sm;
  742. for (L2RecipeInstance recipe : recipes)
  743. {
  744. int quantity = _recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity();
  745. if (quantity > 0)
  746. {
  747. L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
  748. long itemQuantityAmount = item == null ? 0 : item.getCount();
  749. // check materials
  750. if (itemQuantityAmount < quantity)
  751. {
  752. sm = SystemMessage.getSystemMessage(SystemMessageId.MISSING_S2_S1_TO_CREATE);
  753. sm.addItemName(recipe.getItemId());
  754. sm.addItemNumber(quantity - itemQuantityAmount);
  755. _target.sendPacket(sm);
  756. abort();
  757. return null;
  758. }
  759. // make new temporary object, just for counting purposes
  760. TempItem temp = new TempItem(item, quantity);
  761. materials.add(temp);
  762. }
  763. }
  764. if (remove)
  765. {
  766. for (TempItem tmp : materials)
  767. {
  768. inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player);
  769. if (tmp.getQuantity() > 1)
  770. {
  771. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
  772. sm.addItemName(tmp.getItemId());
  773. sm.addItemNumber(tmp.getQuantity());
  774. _target.sendPacket(sm);
  775. }
  776. else
  777. {
  778. sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
  779. sm.addItemName(tmp.getItemId());
  780. _target.sendPacket(sm);
  781. }
  782. }
  783. }
  784. return materials;
  785. }
  786. private void abort()
  787. {
  788. updateMakeInfo(false);
  789. _player.isInCraftMode(false);
  790. _activeMakers.remove(_player.getObjectId());
  791. }
  792. private void rewardPlayer()
  793. {
  794. int rareProdId = _recipeList.getRareItemId();
  795. int itemId = _recipeList.getItemId();
  796. int itemCount = _recipeList.getCount();
  797. L2Item template = ItemTable.getInstance().getTemplate(itemId);
  798. // check that the current recipe has a rare production or not
  799. if ((rareProdId != -1) && ((rareProdId == itemId) || Config.CRAFT_MASTERWORK))
  800. {
  801. if (Rnd.get(100) < _recipeList.getRarity())
  802. {
  803. itemId = rareProdId;
  804. itemCount = _recipeList.getRareCount();
  805. }
  806. }
  807. _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player);
  808. // inform customer of earned item
  809. SystemMessage sm = null;
  810. if (_target != _player)
  811. {
  812. // inform manufacturer of earned profit
  813. if (itemCount == 1)
  814. {
  815. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA);
  816. sm.addString(_target.getName());
  817. sm.addItemName(itemId);
  818. sm.addItemNumber(_price);
  819. _player.sendPacket(sm);
  820. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA);
  821. sm.addString(_player.getName());
  822. sm.addItemName(itemId);
  823. sm.addItemNumber(_price);
  824. _target.sendPacket(sm);
  825. }
  826. else
  827. {
  828. sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA);
  829. sm.addString(_target.getName());
  830. sm.addNumber(itemCount);
  831. sm.addItemName(itemId);
  832. sm.addItemNumber(_price);
  833. _player.sendPacket(sm);
  834. sm = SystemMessage.getSystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_S4_ADENA);
  835. sm.addString(_player.getName());
  836. sm.addNumber(itemCount);
  837. sm.addItemName(itemId);
  838. sm.addItemNumber(_price);
  839. _target.sendPacket(sm);
  840. }
  841. }
  842. if (itemCount > 1)
  843. {
  844. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  845. sm.addItemName(itemId);
  846. sm.addItemNumber(itemCount);
  847. _target.sendPacket(sm);
  848. }
  849. else
  850. {
  851. sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  852. sm.addItemName(itemId);
  853. _target.sendPacket(sm);
  854. }
  855. if (Config.ALT_GAME_CREATION)
  856. {
  857. int recipeLevel = _recipeList.getLevel();
  858. if (_exp < 0)
  859. {
  860. _exp = template.getReferencePrice() * itemCount;
  861. _exp /= recipeLevel;
  862. }
  863. if (_sp < 0)
  864. {
  865. _sp = _exp / 10;
  866. }
  867. if (itemId == rareProdId)
  868. {
  869. _exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  870. _sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE;
  871. }
  872. if (_exp < 0)
  873. {
  874. _exp = 0;
  875. }
  876. if (_sp < 0)
  877. {
  878. _sp = 0;
  879. }
  880. for (int i = _skillLevel; i > recipeLevel; i--)
  881. {
  882. _exp /= 4;
  883. _sp /= 4;
  884. }
  885. // Added multiplication of Creation speed with XP/SP gain
  886. // slower crafting -> more XP, faster crafting -> less XP
  887. // you can use ALT_GAME_CREATION_XP_RATE/SP to
  888. // modify XP/SP gained (default = 1)
  889. _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));
  890. }
  891. updateMakeInfo(true); // success
  892. }
  893. }
  894. private L2RecipeList getValidRecipeList(L2PcInstance player, int id)
  895. {
  896. L2RecipeList recipeList = getRecipeList(id);
  897. if ((recipeList == null) || (recipeList.getRecipes().length == 0))
  898. {
  899. player.sendMessage("No recipe for: " + id);
  900. player.isInCraftMode(false);
  901. return null;
  902. }
  903. return recipeList;
  904. }
  905. private static class SingletonHolder
  906. {
  907. protected static final RecipeController _instance = new RecipeController();
  908. }
  909. }