Java tutorial
/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package com.l2jfree.gameserver.datatables; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.WeakHashMap; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javolution.util.FastList; import javolution.util.FastMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; import com.l2jfree.Config; import com.l2jfree.gameserver.ThreadPoolManager; import com.l2jfree.gameserver.gameobjects.L2Player; import com.l2jfree.gameserver.gameobjects.itemcontainer.Inventory; import com.l2jfree.gameserver.instancemanager.GameTimeManager; import com.l2jfree.gameserver.model.items.L2ItemInstance; import com.l2jfree.gameserver.model.items.manufacture.L2ManufactureItem; import com.l2jfree.gameserver.model.items.recipe.L2RecipeInstance; import com.l2jfree.gameserver.model.items.recipe.L2RecipeList; import com.l2jfree.gameserver.model.items.recipe.L2RecipeStatInstance; import com.l2jfree.gameserver.model.items.templates.L2Item; import com.l2jfree.gameserver.model.skills.L2Skill; import com.l2jfree.gameserver.model.skills.Stats; import com.l2jfree.gameserver.network.SystemMessageId; import com.l2jfree.gameserver.network.packets.server.ActionFailed; import com.l2jfree.gameserver.network.packets.server.ItemList; import com.l2jfree.gameserver.network.packets.server.MagicSkillUse; import com.l2jfree.gameserver.network.packets.server.RecipeBookItemList; import com.l2jfree.gameserver.network.packets.server.RecipeItemMakeInfo; import com.l2jfree.gameserver.network.packets.server.RecipeShopItemInfo; import com.l2jfree.gameserver.network.packets.server.SetupGauge; import com.l2jfree.gameserver.network.packets.server.StatusUpdate; import com.l2jfree.gameserver.network.packets.server.SystemMessage; import com.l2jfree.gameserver.templates.StatsSet; import com.l2jfree.gameserver.util.Util; import com.l2jfree.tools.random.Rnd; public class RecipeTable { private static final String RECIPES_FILE = "recipes.xml"; private final static Log _log = LogFactory.getLog(RecipeTable.class); private static final Map<L2Player, RecipeItemMaker> _activeMakers = Collections .synchronizedMap(new WeakHashMap<L2Player, RecipeItemMaker>()); private final FastMap<Integer, L2RecipeList> _lists; private int[] _itemIds; public static RecipeTable getInstance() { return SingletonHolder._instance; } private RecipeTable() { _lists = new FastMap<Integer, L2RecipeList>().setShared(true); try { loadFromXML(); _log.info("RecipeController: Loaded " + getRecipesCount() + " recipes."); } catch (Exception e) { _log.fatal("Failed loading recipe list", e); } _itemIds = new int[_lists.size()]; int i = 0; for (L2RecipeList rec : _lists.values()) _itemIds[i++] = rec.getRecipeId(); } public int getRecipesCount() { return _lists.size(); } public L2RecipeList getRecipeList(int listId) { return _lists.get(listId); } public L2RecipeList getRecipeByItemId(int itemId) { for (FastMap.Entry<Integer, L2RecipeList> entry = _lists.head(), end = _lists .tail(); (entry = entry.getNext()) != end;) { L2RecipeList rl = entry.getValue(); if (rl.getRecipeId() == itemId) return rl; } return null; } public int[] getAllItemIds() { return _itemIds; } public synchronized void requestBookOpen(L2Player player, boolean isDwarvenCraft) { RecipeItemMaker maker = null; if (Config.ALT_GAME_CREATION) maker = _activeMakers.get(player); if (maker == null) { RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp()); response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook()); player.sendPacket(response); return; } player.sendPacket(SystemMessageId.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING); } public synchronized void requestMakeItemAbort(L2Player player) { _activeMakers.remove(player); // TODO: anything else here? } public synchronized void requestManufactureItem(L2Player manufacturer, int recipeListId, L2Player player) { L2RecipeList recipeList = getValidRecipeList(player, recipeListId); if (recipeList == null) return; List<L2RecipeList> dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook()); List<L2RecipeList> commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook()); if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList)) { Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH); return; } RecipeItemMaker maker; if (Config.ALT_GAME_CREATION && (maker = _activeMakers.get(manufacturer)) != null) // check if busy { player.sendMessage("Manufacturer is busy, please try later."); return; } maker = new RecipeItemMaker(manufacturer, recipeList, player); if (maker._isValid) { if (Config.ALT_GAME_CREATION) { _activeMakers.put(manufacturer, maker); ThreadPoolManager.getInstance().scheduleGeneral(maker, 100); } else maker.run(); } } public synchronized void requestMakeItem(L2Player player, int recipeListId) { if (player.isInDuel()) { player.sendPacket(SystemMessageId.CANT_CRAFT_DURING_COMBAT); return; } L2RecipeList recipeList = getValidRecipeList(player, recipeListId); if (recipeList == null) return; List<L2RecipeList> dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook()); List<L2RecipeList> commonRecipes = Arrays.asList(player.getCommonRecipeBook()); if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList)) { Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH); return; } RecipeItemMaker maker; // check if already busy (possible in alt mode only) if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(player)) != null)) { SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1); sm.addItemName(recipeList.getItemId()); sm.addString("You are busy creating"); player.sendPacket(sm); return; } maker = new RecipeItemMaker(player, recipeList, player); if (maker._isValid) { if (Config.ALT_GAME_CREATION) { _activeMakers.put(player, maker); ThreadPoolManager.getInstance().scheduleGeneral(maker, 100); } else maker.run(); } } private void loadFromXML() throws SAXException, IOException, ParserConfigurationException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); factory.setIgnoringComments(true); File file = new File(Config.DATAPACK_ROOT, "data/" + RECIPES_FILE); if (file.exists()) { Document doc = factory.newDocumentBuilder().parse(file); List<L2RecipeInstance> recipePartList = new FastList<L2RecipeInstance>(); List<L2RecipeStatInstance> recipeStatUseList = new FastList<L2RecipeStatInstance>(); List<L2RecipeStatInstance> recipeAltStatChangeList = new FastList<L2RecipeStatInstance>(); for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) { if ("list".equalsIgnoreCase(n.getNodeName())) { recipesFile: for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling()) { if ("item".equalsIgnoreCase(d.getNodeName())) { recipePartList.clear(); recipeStatUseList.clear(); recipeAltStatChangeList.clear(); NamedNodeMap attrs = d.getAttributes(); Node att; int id = -1; boolean haveRare = false; StatsSet set = new StatsSet(); att = attrs.getNamedItem("id"); if (att == null) { _log.fatal("Missing id for recipe item, skipping"); continue; } id = Integer.parseInt(att.getNodeValue()); set.set("id", id); att = attrs.getNamedItem("recipeId"); if (att == null) { _log.fatal("Missing recipeId for recipe item id: " + id + ", skipping"); continue; } set.set("recipeId", Integer.parseInt(att.getNodeValue())); att = attrs.getNamedItem("name"); if (att == null) { _log.fatal("Missing name for recipe item id: " + id + ", skipping"); continue; } set.set("recipeName", att.getNodeValue()); att = attrs.getNamedItem("craftLevel"); if (att == null) { _log.fatal("Missing level for recipe item id: " + id + ", skipping"); continue; } set.set("craftLevel", Integer.parseInt(att.getNodeValue())); att = attrs.getNamedItem("type"); if (att == null) { _log.fatal("Missing type for recipe item id: " + id + ", skipping"); continue; } set.set("isDwarvenRecipe", att.getNodeValue().equalsIgnoreCase("dwarven")); att = attrs.getNamedItem("successRate"); if (att == null) { _log.fatal("Missing successRate for recipe item id: " + id + ", skipping"); continue; } set.set("successRate", Integer.parseInt(att.getNodeValue())); for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling()) { if ("statUse".equalsIgnoreCase(c.getNodeName())) { String statName = c.getAttributes().getNamedItem("name").getNodeValue(); int value = Integer .parseInt(c.getAttributes().getNamedItem("value").getNodeValue()); try { recipeStatUseList.add(new L2RecipeStatInstance(statName, value)); } catch (Exception e) { _log.fatal("Error in StatUse parameter for recipe item id: " + id + ", skipping", e); continue recipesFile; } } else if ("altStatChange".equalsIgnoreCase(c.getNodeName())) { String statName = c.getAttributes().getNamedItem("name").getNodeValue(); int value = Integer .parseInt(c.getAttributes().getNamedItem("value").getNodeValue()); try { recipeAltStatChangeList.add(new L2RecipeStatInstance(statName, value)); } catch (Exception e) { _log.fatal("Error in AltStatChange parameter for recipe item id: " + id + ", skipping", e); continue recipesFile; } } else if ("ingredient".equalsIgnoreCase(c.getNodeName())) { int ingId = Integer .parseInt(c.getAttributes().getNamedItem("id").getNodeValue()); int ingCount = Integer .parseInt(c.getAttributes().getNamedItem("count").getNodeValue()); recipePartList.add(new L2RecipeInstance(ingId, ingCount)); } else if ("production".equalsIgnoreCase(c.getNodeName())) { set.set("itemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue())); set.set("count", Integer .parseInt(c.getAttributes().getNamedItem("count").getNodeValue())); } else if ("productionRare".equalsIgnoreCase(c.getNodeName())) { set.set("rareItemId", Integer.parseInt(c.getAttributes().getNamedItem("id").getNodeValue())); set.set("rareCount", Integer .parseInt(c.getAttributes().getNamedItem("count").getNodeValue())); set.set("rarity", Integer .parseInt(c.getAttributes().getNamedItem("rarity").getNodeValue())); haveRare = true; } } L2RecipeList recipeList = new L2RecipeList(set, haveRare); for (L2RecipeInstance recipePart : recipePartList) recipeList.addRecipe(recipePart); for (L2RecipeStatInstance recipeStatUse : recipeStatUseList) recipeList.addStatUse(recipeStatUse); for (L2RecipeStatInstance recipeAltStatChange : recipeAltStatChangeList) recipeList.addAltStatChange(recipeAltStatChange); _lists.put(recipeList.getId(), recipeList); } } } } } else { _log.fatal("Recipes file (" + file.getAbsolutePath() + ") doesnt exists."); } } private class RecipeItemMaker implements Runnable { protected boolean _isValid; protected List<TempItem> _items = null; protected final L2RecipeList _recipeList; protected final L2Player _player; // "crafter" protected final L2Player _target; // "customer" protected final L2Skill _skill; protected final int _skillId; protected final int _skillLevel; protected int _creationPasses = 1; protected int _itemGrab; protected long _exp = -1; protected int _sp = -1; protected long _price; protected int _totalItems; protected long _materialsRefPrice; protected int _delay; public RecipeItemMaker(L2Player pPlayer, L2RecipeList pRecipeList, L2Player pTarget) { _player = pPlayer; _target = pTarget; _recipeList = pRecipeList; _isValid = false; _skillId = _recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN : L2Skill.SKILL_CREATE_COMMON; _skillLevel = _player.getSkillLevel(_skillId); _skill = _player.getKnownSkill(_skillId); _player.isInCraftMode(true); if (_player.isAlikeDead()) { _player.sendMessage("Dead people don't craft."); _player.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } if (_target.isAlikeDead()) { _target.sendMessage("Dead customers can't use manufacture."); _target.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } if (_target.isProcessingTransaction()) { _target.sendMessage("You are busy."); _target.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } if (_player.isProcessingTransaction()) { if (_player != _target) { _target.sendMessage("Manufacturer " + _player.getName() + " is busy."); } _player.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } // validate recipe list if (_recipeList.getRecipes().length == 0) { _player.sendMessage("No such recipe"); _player.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } // validate skill level if (_recipeList.getLevel() > _skillLevel) { _player.sendMessage("Need skill level " + _recipeList.getLevel()); _player.sendPacket(ActionFailed.STATIC_PACKET); abort(); return; } // check that customer can afford to pay for creation services if (_player != _target) { for (L2ManufactureItem temp : _player.getCreateList().getList()) if (temp.getRecipeId() == _recipeList.getId()) // find recipe for item we want manufactured { _price = temp.getCost(); if (_target.getAdena() < _price) // check price { _target.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA); abort(); return; } break; } } // make temporary items if ((_items = listItems(false)) == null) { abort(); return; } // calculate reference price for (TempItem i : _items) { _materialsRefPrice += i.getReferencePrice() * i.getQuantity(); _totalItems += i.getQuantity(); } // initial statUse checks if (!calculateStatUse(false, false)) { abort(); return; } // initial AltStatChange checks if (Config.ALT_GAME_CREATION) calculateAltStatChange(); updateMakeInfo(true); updateCurMp(); updateCurLoad(); _isValid = true; } @Override public void run() { if (!Config.ALT_IS_CRAFTING_ENABLED) { _target.sendMessage("Item creation is currently disabled."); abort(); return; } if (_player == null || _target == null) { _log.warn("player or target == null (disconnected?), aborting" + _target + _player); abort(); return; } if (_player.isOnline() == 0 || _target.isOnline() == 0) { _log.warn("player or target is not online, aborting " + _target + _player); abort(); return; } if (Config.ALT_GAME_CREATION && _activeMakers.get(_player) == null) { if (_target != _player) { _target.sendMessage("Manufacture aborted"); _player.sendMessage("Manufacture aborted"); } else { _player.sendMessage("Item creation aborted"); } abort(); return; } if (Config.ALT_GAME_CREATION && !_items.isEmpty()) { if (!calculateStatUse(true, true)) return; // check stat use updateCurMp(); // update craft window mp bar grabSomeItems(); // grab (equip) some more items with a nice msg to player // if still not empty, schedule another pass if (!_items.isEmpty()) { // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumables rates _delay = (int) (Config.ALT_GAME_CREATION_SPEED * _player.getStat().getMReuseRate(_skill) * GameTimeManager.TICKS_PER_SECOND / Config.RATE_CONSUMABLE_COST) * GameTimeManager.MILLIS_IN_TICK; // FIXME: please fix this packet to show crafting animation (somebody) MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0); _player.broadcastPacket(msk); _player.sendPacket(new SetupGauge(0, _delay)); ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); } else { // for alt mode, sleep delay msec before finishing _player.sendPacket(new SetupGauge(0, _delay)); ThreadPoolManager.getInstance().schedule(new Runnable() { @Override public void run() { finishCrafting(); } }, _delay); } } // for old craft mode just finish else finishCrafting(); } private void finishCrafting() { if (!Config.ALT_GAME_CREATION) calculateStatUse(false, true); // first take adena for manufacture if ((_target != _player) && _price > 0) // customer must pay for services { // attempt to pay for item L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player); if (adenatransfer == null) { _target.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA); abort(); return; } } boolean success = false; if ((_items = listItems(true)) == null) // this line actually takes materials from inventory { // handle possible cheaters here // (they click craft then try to get rid of items in order to get free craft) } else if (_recipeList.getSuccessRate() == 100 || Rnd.get(100) < _recipeList.getSuccessRate()) { rewardPlayer(_price); // and immediately puts created item in its place success = true; } else { if (_target != _player) { SystemMessage msg = new SystemMessage(SystemMessageId.CREATION_OF_S2_FOR_C1_AT_S3_ADENA_FAILED); msg.addPcName(_target); msg.addItemName(_recipeList.getItemId()); msg.addItemNumber(_price); _player.sendPacket(msg); msg = new SystemMessage(SystemMessageId.C1_FAILED_TO_CREATE_S2_FOR_S3_ADENA); msg.addPcName(_player); msg.addItemName(_recipeList.getItemId()); msg.addItemNumber(_price); _target.sendPacket(msg); } else { _player.sendPacket(SystemMessageId.ITEM_MIXING_FAILED); } } // update load and mana bar of craft window updateCurMp(); updateCurLoad(); _activeMakers.remove(_player); _player.isInCraftMode(false); _target.sendPacket(new ItemList(_target, false)); updateMakeInfo(success); } private void updateMakeInfo(boolean success) { if (_target == _player) _target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success)); else _target.sendPacket(new RecipeShopItemInfo(_player, _recipeList.getId())); } private void updateCurLoad() { StatusUpdate su = new StatusUpdate(_target.getObjectId()); su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad()); _target.sendPacket(su); } private void updateCurMp() { StatusUpdate su = new StatusUpdate(_target.getObjectId()); su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getStatus().getCurrentMp()); _target.sendPacket(su); } private void grabSomeItems() { int grabItems = _itemGrab; while (grabItems > 0 && !_items.isEmpty()) { TempItem item = _items.get(0); long count = item.getQuantity(); if (count >= grabItems) count = grabItems; item.setQuantity(item.getQuantity() - count); if (item.getQuantity() <= 0) _items.remove(0); else _items.set(0, item); grabItems -= count; SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2_EQUIPPED); // you equipped ... sm.addItemNumber(count); sm.addItemName(item.getItemId()); _player.sendPacket(sm); if (_target != _player) _target.sendMessage( "Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName()); } } // AltStatChange parameters make their effect here private void calculateAltStatChange() { _itemGrab = _skillLevel; for (L2RecipeStatInstance altStatChange : _recipeList.getAltStatChange()) { if (altStatChange.getType() == L2RecipeStatInstance.statType.XP) _exp = altStatChange.getValue(); else if (altStatChange.getType() == L2RecipeStatInstance.statType.SP) _sp = altStatChange.getValue(); else if (altStatChange.getType() == L2RecipeStatInstance.statType.GIM) _itemGrab *= altStatChange.getValue(); } // determine number of creation passes needed _creationPasses = (_totalItems / _itemGrab) + ((_totalItems % _itemGrab) != 0 ? 1 : 0); if (_creationPasses < 1) _creationPasses = 1; } // StatUse private boolean calculateStatUse(boolean isWait, boolean isReduce) { boolean ret = true; for (L2RecipeStatInstance statUse : _recipeList.getStatUse()) { double modifiedValue = statUse.getValue() / _creationPasses; if (statUse.getType() == L2RecipeStatInstance.statType.HP) { // we do not want to kill the player, so its CurrentHP must be greater than the reduce value if (_player.getStatus().getCurrentHp() <= modifiedValue) { // rest (wait for HP) if (Config.ALT_GAME_CREATION && isWait) { _player.sendPacket(new SetupGauge(0, _delay)); ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); } else // no rest - report no hp { _target.sendPacket(SystemMessageId.NOT_ENOUGH_HP); abort(); } ret = false; } else if (isReduce) _player.reduceCurrentHpByConsume(modifiedValue); } else if (statUse.getType() == L2RecipeStatInstance.statType.MP) { if (_player.getStatus().getCurrentMp() < modifiedValue) { // rest (wait for MP) if (Config.ALT_GAME_CREATION && isWait) { _player.sendPacket(new SetupGauge(0, _delay)); ThreadPoolManager.getInstance().scheduleGeneral(this, 100 + _delay); } else // no rest - report no mana { _target.sendPacket(SystemMessageId.NOT_ENOUGH_MP); abort(); } ret = false; } else if (isReduce) _player.reduceCurrentMp(modifiedValue); } else { // there is an unknown StatUse value _target.sendMessage("Recipe error!!!, please tell this to your GM."); ret = false; abort(); } } return ret; } private List<TempItem> listItems(boolean remove) { L2RecipeInstance[] recipes = _recipeList.getRecipes(); Inventory inv = _target.getInventory(); List<TempItem> materials = new FastList<TempItem>(); SystemMessage sm; for (L2RecipeInstance recipe : recipes) { long quantity = _recipeList.isConsumable() ? (long) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity(); if (quantity > 0) { L2ItemInstance item = inv.getItemByItemId(recipe.getItemId()); long itemQuantityAmount = item == null ? 0 : item.getCount(); // check materials if (itemQuantityAmount < quantity) { sm = new SystemMessage(SystemMessageId.MISSING_S2_S1_TO_CREATE); sm.addItemName(recipe.getItemId()); sm.addItemNumber(quantity - itemQuantityAmount); _target.sendPacket(sm); abort(); return null; } // make new temporary object, just for counting purposes TempItem temp = new TempItem(item, quantity); materials.add(temp); } } if (remove) { for (TempItem tmp : materials) { inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player); } } return materials; } private void abort() { updateMakeInfo(false); _player.isInCraftMode(false); _activeMakers.remove(_player); } /** * FIXME: This class should be in some other file, but I don't know * where Class explanation: For item counting or checking purposes. When * you don't want to modify inventory class contains itemId, quantity, * ownerId, referencePrice, but not objectId */ private class TempItem { // no object id stored, this will be only "list" of items with it's owner private final int _itemId; private long _quantity; // private final int _ownerId; private final int _referencePrice; private final String _itemName; /** * @param item * @param quantity of that item */ public TempItem(L2ItemInstance item, long quantity) { super(); _itemId = item.getItemId(); _quantity = quantity; // _ownerId = item.getOwnerId(); _itemName = item.getItem().getName(); _referencePrice = item.getReferencePrice(); } /** * @return Returns the quantity. */ public long getQuantity() { return _quantity; } /** * @param quantity The quantity to set. */ public void setQuantity(long quantity) { _quantity = quantity; } public long getReferencePrice() { return _referencePrice; } /** * @return Returns the itemId. */ public int getItemId() { return _itemId; } // /** // * @return Returns the ownerId. // */ // public int getOwnerId() // { // return _ownerId; // } /** * @return Returns the itemName. */ public String getItemName() { return _itemName; } } private void rewardPlayer(long price) { int rareProdId = _recipeList.getRareItemId(); int itemId = _recipeList.getItemId(); long itemCount = _recipeList.getCount(); L2Item template = ItemTable.getInstance().getTemplate(itemId); if (Config.ALT_MASTERWORK_CONFIG) { if (Config.ALLOW_MASTERWORK) { if (rareProdId > 0 && Rnd.get(100) < Config.RATE_MASTERWORK) { itemId = rareProdId; } } if (Config.ALLOW_CRITICAL_CRAFT && _recipeList.isCriticalAffected()) { if (Rnd.get(100) < Config.RATE_CRITICAL_CRAFT_CHANCE) { itemCount = itemCount * Config.RATE_CRITICAL_CRAFT_MULTIPLIER; } } } else { if (rareProdId > 0 && Config.ALLOW_MASTERWORK) { if (Rnd.get(100) < _recipeList.getRarity()) { itemId = rareProdId; if (Config.ALLOW_CRITICAL_CRAFT) { itemCount = _recipeList.getRareCount(); } } } } _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player); SystemMessage msg; if (_target != _player) { // inform manufacturer of earned profit if (itemCount == 1) { msg = new SystemMessage(SystemMessageId.S2_CREATED_FOR_C1_FOR_S3_ADENA); msg.addPcName(_target); msg.addItemName(template); msg.addItemNumber(price); _player.sendPacket(msg); msg = new SystemMessage(SystemMessageId.C1_CREATED_S2_FOR_S3_ADENA); msg.addPcName(_player); msg.addItemName(template); msg.addItemNumber(price); _target.sendPacket(msg); } else { msg = new SystemMessage(SystemMessageId.S2_S3_S_CREATED_FOR_C1_FOR_S4_ADENA); msg.addPcName(_target); msg.addItemNumber(itemCount); msg.addItemName(template); msg.addItemNumber(price); _player.sendPacket(msg); msg = new SystemMessage(SystemMessageId.C1_CREATED_S2_S3_S_FOR_S4_ADENA); msg.addPcName(_player); msg.addItemNumber(itemCount); msg.addItemName(template); msg.addItemNumber(price); _target.sendPacket(msg); } } // inform customer of earned item if (itemCount > 1) { msg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S); msg.addItemName(template); msg.addItemNumber(itemCount); _target.sendPacket(msg); } else { msg = new SystemMessage(SystemMessageId.EARNED_S1); msg.addItemName(template); _target.sendPacket(msg); } if (Config.ALT_GAME_CREATION) { int recipeLevel = _recipeList.getLevel(); if (_exp < 0) { _exp = template.getReferencePrice() * itemCount; _exp /= recipeLevel; } if (_sp < 0) _sp = (int) (_exp / 10); if (itemId == rareProdId) { _exp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE; _sp *= Config.ALT_GAME_CREATION_RARE_XPSP_RATE; } // one variation // exp -= materialsRefPrice; // mat. ref. price is not accurate so other method is better if (_exp < 0) _exp = 0; if (_sp < 0) _sp = 0; for (int i = _skillLevel; i > recipeLevel; i--) { _exp /= 4; _sp /= 4; } // Added multiplication of Creation speed with XP/SP gain // slower crafting -> more XP, faster crafting -> less XP // you can use ALT_GAME_CREATION_XP_RATE/SP to // modify XP/SP gained (default = 1) _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)); } updateMakeInfo(true); // success } } private L2RecipeList getValidRecipeList(L2Player player, int id) { L2RecipeList recipeList = getRecipeList(id); if ((recipeList == null) || (recipeList.getRecipes().length == 0)) { player.sendMessage("No recipe for: " + id); player.isInCraftMode(false); return null; } return recipeList; } @SuppressWarnings("synthetic-access") private static class SingletonHolder { protected static final RecipeTable _instance = new RecipeTable(); } }