RecipeController.java 33 KB

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