2
0

RecipeController.java 30 KB

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