Index: config/altsettings.properties =================================================================== --- config/altsettings.properties (revision 1682) +++ config/altsettings.properties (working copy) @@ -23,9 +23,13 @@ # - players get EXP/SP for crafting AltGameCreation = False -# time multiplyer - bigger number = slower crafting, default was 15 now is 10 -AltGameCreationSpeed = 10 +# time multiplier - bigger number ===> slower crafting but more XP/SP +AltGameCreationSpeed = 1 +# Additional XP/SP rate multiplier (for increased XP/SP) default = 1 +AltGameCreationRateXp = 1 +AltGameCreationRateSp = 1 + # Alternative skill learn rules: # - all classes can learn all skills # - skills of another class costs x2 SP Index: net/sf/l2j/Config.java =================================================================== --- net/sf/l2j/Config.java (revision 1682) +++ net/sf/l2j/Config.java (working copy) @@ -80,15 +80,21 @@ /** Character name template */ public static String CNAME_TEMPLATE; - + /** Global chat state */ public static String DEFAULT_GLOBAL_CHAT; /** For test servers - evrybody has admin rights */ public static boolean EVERYBODY_HAS_ADMIN_RIGHTS; /** Alternative game crafting */ public static boolean ALT_GAME_CREATION; - /** Alternative game crafting speed mutiplyer - default 10, bigger number -> slower */ - public static int ALT_GAME_CREATION_SPEED; + /** Alternative game crafting speed mutiplier - default 0 (fastest but still not instant) */ + public static double ALT_GAME_CREATION_SPEED; + /** Alternative game crafting XP rate multiplier - default 1*/ + public static double ALT_GAME_CREATION_XP_RATE; + /** Alternative game crafting SP rate multiplier - default 1*/ + public static double ALT_GAME_CREATION_SP_RATE; + + /** Alternative game skill learning */ public static boolean ALT_GAME_SKILL_LEARN; /** Cancel ATTACK/cast by hit */ @@ -234,7 +240,7 @@ public static int PLAYER_RATE_DROP_ITEM; public static int PLAYER_RATE_DROP_EQUIP; public static int PLAYER_RATE_DROP_EQUIP_WEAPON; - + /** Pet Rates (Multipliers) */ public static float PET_XP_RATE; public static int PET_FOOD_RATE; @@ -322,7 +328,7 @@ public static final String HEXID_FILE = "./config/hexid.txt"; public static final String COMMAND_PRIVILEGES_FILE = "./config/command-privileges.properties"; public static final String AI_FILE = "./config/ai.properties"; - + public static boolean CHECK_KNOWN; public static int GAME_SERVER_LOGIN_PORT; @@ -577,7 +583,7 @@ PLAYER_RATE_DROP_ITEM = Integer.parseInt(serverSettings.getProperty("PlayerRateDropItem", "70")); PLAYER_RATE_DROP_EQUIP = Integer.parseInt(serverSettings.getProperty("PlayerRateDropEquip", "25")); PLAYER_RATE_DROP_EQUIP_WEAPON = Integer.parseInt(serverSettings.getProperty("PlayerRateDropEquipWeapon", "5")); - + PET_XP_RATE = Float.parseFloat(serverSettings.getProperty("PetXpRate", "1.")); PET_FOOD_RATE = Integer.parseInt(serverSettings.getProperty("PetFoodRate", "1")); @@ -834,6 +840,7 @@ GM_NAME_COLOUR = Integer.decode("0x" + otherSettings.getProperty("GMNameColour", "00FF00")); GM_STARTUP_INVISIBLE = Boolean.parseBoolean(otherSettings.getProperty("GMStartupInvisible", "True")); GM_STARTUP_SILENCE = Boolean.parseBoolean(otherSettings.getProperty("GMStartupSilence", "True")); + } catch (Exception e) { @@ -873,13 +880,15 @@ ALT_GAME_TIREDNESS = Boolean.parseBoolean(altSettings.getProperty("AltGameTiredness", "false")); ALT_GAME_CREATION = Boolean.parseBoolean(altSettings.getProperty("AltGameCreation", "false")); - ALT_GAME_CREATION_SPEED = Integer.parseInt(altSettings.getProperty("AltGameCreationSpeed", "10")); + ALT_GAME_CREATION_SPEED = Double.parseDouble(altSettings.getProperty("AltGameCreationSpeed", "1")); + ALT_GAME_CREATION_XP_RATE=Double.parseDouble(altSettings.getProperty("AltGameCreationXpRate", "1")); + ALT_GAME_CREATION_SP_RATE=Double.parseDouble(altSettings.getProperty("AltGameCreationSpRate", "1")); ALT_GAME_SKILL_LEARN = Boolean.parseBoolean(altSettings.getProperty("AltGameSkillLearn", "false")); ALT_GAME_CANCEL_BOW = altSettings.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("bow") || altSettings.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("all"); ALT_GAME_CANCEL_CAST = altSettings.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("cast") || altSettings.getProperty("AltGameCancelByHit", "Cast").equalsIgnoreCase("all"); ALT_GAME_SHIELD_BLOCKS = Boolean.parseBoolean(altSettings.getProperty("AltShieldBlocks", "false")); ALT_GAME_DELEVEL = Boolean.parseBoolean(altSettings.getProperty("Delevel", "true")); - ALT_GAME_MAGICFAILURES = Boolean.parseBoolean(altSettings.getProperty("MagicFailures", "false")); + ALT_GAME_MAGICFAILURES = Boolean.parseBoolean(altSettings.getProperty("MagicFailures", "false")); ALT_GAME_MOB_ATTACK_AI = Boolean.parseBoolean(altSettings.getProperty("AltGameMobAttackAI", "false")); ALT_GAME_SKILL_FORMULAS = altSettings.getProperty("AltGameSkillFormulas", "none"); ALT_GAME_EXPONENT_XP = Float.parseFloat(altSettings.getProperty("AltGameExponentXp", "0.")); @@ -1232,7 +1241,9 @@ // Alternative settings else if (pName.equalsIgnoreCase("AltGameTiredness")) ALT_GAME_TIREDNESS = Boolean.valueOf(pValue); else if (pName.equalsIgnoreCase("AltGameCreation")) ALT_GAME_CREATION = Boolean.valueOf(pValue); - else if (pName.equalsIgnoreCase("AltGameCreationSpeed")) ALT_GAME_CREATION_SPEED = Integer.parseInt(pValue); + else if (pName.equalsIgnoreCase("AltGameCreationSpeed")) ALT_GAME_CREATION_SPEED = Double.parseDouble(pValue); + else if (pName.equalsIgnoreCase("AltGameCreationXpRate")) ALT_GAME_CREATION_XP_RATE = Double.parseDouble(pValue); + else if (pName.equalsIgnoreCase("AltGameCreationSpRate")) ALT_GAME_CREATION_SP_RATE = Double.parseDouble(pValue); else if (pName.equalsIgnoreCase("AltGameSkillLearn")) ALT_GAME_SKILL_LEARN = Boolean.valueOf(pValue); else if (pName.equalsIgnoreCase("AltGameCancelByHit")) Index: net/sf/l2j/gameserver/RecipeController.java =================================================================== --- net/sf/l2j/gameserver/RecipeController.java (revision 1682) +++ net/sf/l2j/gameserver/RecipeController.java (working copy) @@ -33,8 +33,6 @@ import javolution.util.FastList; import javolution.util.FastMap; import net.sf.l2j.Config; -import net.sf.l2j.gameserver.ClientScheduler; -import net.sf.l2j.gameserver.GameTimeController; import net.sf.l2j.gameserver.lib.Rnd; import net.sf.l2j.gameserver.model.Inventory; import net.sf.l2j.gameserver.model.L2ItemInstance; @@ -57,51 +55,49 @@ public class RecipeController { protected static Logger _log = Logger.getLogger(RecipeController.class.getName()); - + private static RecipeController _instance; private Map _lists; protected static Map activeMakers = Collections.synchronizedMap(new WeakHashMap()); protected static final Random _rnd = new Random(); - + public static RecipeController getInstance() { return _instance == null ? _instance = new RecipeController() : _instance; } - + public RecipeController() { _lists = new FastMap(); String line = null; LineNumberReader lnr = null; - + try { File recipesData = new File(Config.DATAPACK_ROOT, "data/recipes.csv"); lnr = new LineNumberReader(new BufferedReader(new FileReader(recipesData))); - + while ((line = lnr.readLine()) != null) { if (line.trim().length() == 0 || line.startsWith("#")) - { continue; - } - + parseList(line); - + } _log.config("RecipeController: Loaded " + _lists.size() + " Recipes."); } catch (Exception e) { if (lnr != null) - { + _log.log(Level.WARNING, "error while creating recipe controller in linenr: " - + lnr.getLineNumber(), e); - } + + lnr.getLineNumber(), e); + else - { + _log.warning("No recipes were found in data folder"); - } + } finally { @@ -114,17 +110,17 @@ } } } - + public int getRecipesCount() { return _lists.size(); } - + public L2RecipeList getRecipeList(int listId) { return _lists.get(listId); } - + public L2RecipeList getRecipeByItemId(int itemId) { for (int i = 0; i < _lists.size(); i++) @@ -137,112 +133,118 @@ } return null; } - + public synchronized void requestBookOpen(L2PcInstance 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.getCommonRecipeBook()); player.sendPacket(response); return; } - + SystemMessage sm = new SystemMessage(SystemMessage.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING); player.sendPacket(sm); return; } - + public synchronized void requestMakeItemAbort(L2PcInstance player) { - activeMakers.remove(player); + if (activeMakers != null) activeMakers.remove(player); } - + public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, - L2PcInstance player) + L2PcInstance player) { L2RecipeList recipeList = getValidRecipeList(player, recipeListId); - + if (recipeList == null) return; - manufacturer.isInCraftMode(true); - - if (!Config.ALT_GAME_CREATION) + + RecipeItemMaker maker; + + if (Config.ALT_GAME_CREATION && (maker = activeMakers.get(manufacturer)) != null) // check if busy { - new RecipeItemMaker(manufacturer, recipeList, player); + player.sendMessage("Manufacturer is busy, please try later."); + return; } - else + + maker = new RecipeItemMaker(manufacturer, recipeList, player); + if (maker.isValid) { - RecipeItemMaker maker = activeMakers.get(manufacturer); - if (maker != null) + if (Config.ALT_GAME_CREATION) { - player.sendMessage("Manufacturer is busy creating."); - manufacturer.isInCraftMode(false); - return; + activeMakers.put(manufacturer, maker); + ClientScheduler.scheduleEvent(maker, 100); } - maker = new RecipeItemMaker(manufacturer, recipeList, player); - if (maker.isValid) activeMakers.put(manufacturer, maker); + else maker.run(); } } - + public synchronized void requestMakeItem(L2PcInstance player, int recipeListId) { L2RecipeList recipeList = getValidRecipeList(player, recipeListId); - - if (recipeList == null) return; - - player.isInCraftMode(true); - - if (!Config.ALT_GAME_CREATION) + + if (recipeList == null) { - new RecipeItemMaker(player, recipeList, player); + _log.warning("recipelist == null"); + return; } - else + + RecipeItemMaker maker; + + // check if already busy (possible in alt mode only) + if (Config.ALT_GAME_CREATION && ((maker = activeMakers.get(player)) != null)) { - RecipeItemMaker maker = activeMakers.get(player); - if (maker != null) + SystemMessage sm = new SystemMessage(614); + sm.addString("You are busy creating "); + sm.addItemName(recipeList.getItemId()); + player.sendPacket(sm); + return; + } + + maker = new RecipeItemMaker(player, recipeList, player); + if (maker.isValid) + { + if (Config.ALT_GAME_CREATION) { - SystemMessage sm = new SystemMessage(614); - sm.addString("You are busy creating "); - sm.addItemName(recipeList.getItemId()); - player.sendPacket(sm); - player.isInCraftMode(false); - return; + activeMakers.put(player, maker); + ClientScheduler.scheduleEvent(maker, 100); } - maker = new RecipeItemMaker(player, recipeList, player); - if (maker.isValid) activeMakers.put(player, maker); + else maker.run(); } } - + private void parseList(String line) { try { StringTokenizer st = new StringTokenizer(line, ";"); List recipePartList = new FastList(); - + //we use common/dwarf for easy reading of the recipes.csv file String recipeTypeString = st.nextToken(); - - //now we parse the string into a boolean + + // now parse the string into a boolean boolean isDwarvenRecipe; - + if (recipeTypeString.equalsIgnoreCase("dwarven")) isDwarvenRecipe = true; else if (recipeTypeString.equalsIgnoreCase("common")) isDwarvenRecipe = false; else { //prints a helpfull message - _log.warning("Error parsing recipes.csv, unknow recipe type " + recipeTypeString); + _log.warning("Error parsing recipes.csv, unknown recipe type " + recipeTypeString); return; } - + String recipeName = st.nextToken(); int id = Integer.parseInt(st.nextToken()); int recipeId = Integer.parseInt(st.nextToken()); int level = Integer.parseInt(st.nextToken()); - + //material StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],"); while (st2.hasMoreTokens()) @@ -253,18 +255,18 @@ L2RecipeInstance rp = new L2RecipeInstance(rpItemId, quantity); recipePartList.add(rp); } - + int itemId = Integer.parseInt(st.nextToken()); int count = Integer.parseInt(st.nextToken()); - + //npc fee /*String notdoneyet = */st.nextToken(); - + int mpCost = Integer.parseInt(st.nextToken()); int successRate = Integer.parseInt(st.nextToken()); - + L2RecipeList recipeList = new L2RecipeList(id, level, recipeId, recipeName, successRate, - mpCost, itemId, count, isDwarvenRecipe); + mpCost, itemId, count, isDwarvenRecipe); for (L2RecipeInstance recipePart : recipePartList) { recipeList.addRecipe(recipePart); @@ -275,328 +277,490 @@ { _log.severe("Exception in RecipeController.parseList() - " + e); } - ; } - + public class RecipeItemMaker implements Runnable { public boolean isValid; - final List items; - int materialsRefPrice; - final L2PcInstance player; + List items = null; + final L2RecipeList recipeList; + final L2PcInstance player; // "crafter" + final L2PcInstance target; // "customer" + final L2Skill skill; + final int skillId; + final int skillLevel; + double creationPasses; int price; - final L2RecipeList recipeList; - final L2PcInstance target; - int timeReady; int delay; - int gameTicks; int totalItems; - int usedItems; - int skillLevel; + int materialsRefPrice; + double manaRequired; public RecipeItemMaker(L2PcInstance player, L2RecipeList recipeList, L2PcInstance target) { this.player = player; this.target = target; this.recipeList = recipeList; - this.items = new FastList(); - this.isValid = false; - - skillLevel = recipeList.isDwarvenRecipe() ? player.getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN) - : player.getSkillLevel(L2Skill.SKILL_CREATE_COMMON); - - if (!validateSkillLevel()) return; - if (!validateRecipeList()) return; - if (!validateCustomerCanAfford(target,player)) return; - if (!validateMaterials()) return; - if (!validateMp()) return; - - isValid = true; - updateTime(); - run(); - } - - private void finishCrafting() - { - player.reduceCurrentMp(recipeList.getMpCost()); - - if (Rnd.get(100) < recipeList.getSuccessRate()) + + 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()) { - if (target != player) - { - if (price > 0) + player.sendMessage("Dead persons can't craft."); + player.sendPacket(new ActionFailed()); + abort(); + return; + } + + if (target.isAlikeDead()) + { + target.sendMessage("Dead customers can't use manufacture."); + target.sendPacket(new ActionFailed()); + abort(); + return; + } + + // validate recipe list + if ((recipeList == null) || (recipeList.getRecipes().length == 0)) + { + player.sendMessage("No such recipe"); + player.sendPacket(new ActionFailed()); + abort(); + return; + } + + manaRequired = recipeList.getMpCost(); + + // validate skill level + if (recipeList.getLevel() > skillLevel) + { + player.sendMessage("Need skill level " + recipeList.getLevel()); + player.sendPacket(new ActionFailed()); + 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 { - target.reduceAdena("Manufacture", price, player, false); - player.addAdena("Manufacture", price, target, false); + price = temp.getCost(); + if (target.getAdena() < price) // check price + { + target.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA)); + abort(); + return; + } + break; } - updateCurLoad(); + } + + // make temporary items + if ((items = listItems(false)) == null) + { + abort(); + return; } - - RewardPlayer(); - updateMakeInfo(true); // success + + // calculate reference price + for (TempItem i : items) + { + materialsRefPrice += i.getReferencePrice() * i.getQuantity(); + totalItems += i.getQuantity(); } - else + // initial mana check requires MP as written on recipe + if (player.getCurrentMp() < manaRequired) { - player.sendMessage("Item(s) failed to create"); - updateMakeInfo(false); + target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP)); + abort(); + return; } - activeMakers.remove(player); + + // determine number of creation passes needed + // can "equip" skillLevel items each pass + creationPasses = (totalItems / skillLevel) + ((totalItems % skillLevel)!=0 ? 1 : 0); + + if (Config.ALT_GAME_CREATION && creationPasses != 0) // update mana required to "per pass" + manaRequired /= creationPasses; // checks to validateMp() will only need portion of mp for one pass + + updateMakeInfo(true); // attempt to update mana bar + updateCurMp(); + updateCurLoad(); + player.isInCraftMode(false); - - target.sendPacket(new ItemList(target, false)); + isValid = true; } - + public void run() { - player.isInCraftMode(true); - gameTicks = GameTimeController.getGameTicks(); - if (!validateCraftingEnabled() || !validateMp()) return; + if (!Config.IS_CRAFTING_ENABLED) + { + target.sendMessage("Crafting is currently disabled."); + abort(); + return; + } - updateTime(); + if (player == null || target == null) + { + _log.warning("player or target == null (disconnected?), aborting"); + abort(); + return; + } + if (player.isOnline()==0 || target.isOnline()==0) + { + _log.warning("player or target is not online, aborting"); + abort(); + return; + } + + if (Config.ALT_GAME_CREATION && activeMakers.get(player) == null) + { + player.sendMessage("Crafting aborted"); + abort(); + return; + } + + // check mana + if (!validateMp()) return; + + // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumable rates + // to prevent slower crafting on higher consumable rates + delay = (int) (Config.ALT_GAME_CREATION_SPEED * player.getMReuseRate(skill) + * GameTimeController.TICKS_PER_SECOND / Config.RATE_CONSUMABLE_COST) + * GameTimeController.MILLIS_IN_TICK; + if (Config.ALT_GAME_CREATION && !items.isEmpty()) { - player.reduceCurrentMp(recipeList.getMpCost()); // use some mp + player.reduceCurrentMp(manaRequired); // use some mp grabSomeItems(); // grab (equip) some more items with a nice msg to player - - MagicSkillUser msk = new MagicSkillUser( - player, - recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN - : L2Skill.SKILL_CREATE_COMMON, - skillLevel, delay, 0); - + + // FIXME: please fix this packet to show crafting animation (somebody) + MagicSkillUser msk = new MagicSkillUser(player, skillId, skillLevel, delay, 0); player.broadcastPacket(msk); - + player.sendPacket(new SetupGauge(0, delay)); + + ClientScheduler.scheduleEvent(this, 100 + delay); + _log.warning("."); + } + else + // finish crafting + { + _log.warning("finishcrafting entered"); + if(!Config.ALT_GAME_CREATION) player.reduceCurrentMp(manaRequired); + + // 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 || adenatransfer.getCount()!=price) + { + target.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA)); + abort(); + return; + } + } - ClientScheduler.getInstance().scheduleLow(this, 100 + delay); + _log.warning("calling listItems(true)"); + if ((items = listItems(true)) == null) // this line actually takes materials from inventory + { + _log.warning("Could not take materials from inventory: " + target); + target.sendMessage("Could not take materials from inventory"); + } + else if (Rnd.get(100) < recipeList.getSuccessRate()) + { + RewardPlayer(); // and this one immediately puts created item in its place + updateMakeInfo(true); + } + else + { + player.sendMessage("Item(s) failed to create"); + updateMakeInfo(false); + } + _log.warning("before updates and finish"); + // update load and mana bar of craft window + updateCurMp(); + updateCurLoad(); + activeMakers.remove(player); + player.isInCraftMode(false); + target.sendPacket(new ItemList(target, false)); } - else finishCrafting(); - - // update load and mana bar of craft window - updateMakeInfo(true); - updateCurMp(); - updateCurLoad(); } - - private void updateTime() - { - // divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumable rates (more mats consumed -> more time) - timeReady = gameTicks - + (int) (Config.ALT_GAME_CREATION_SPEED - * player.getMReuseRate(player.getKnownSkill(172)) - * GameTimeController.TICKS_PER_SECOND / Config.RATE_CONSUMABLE_COST); - - delay = (timeReady - gameTicks) * GameTimeController.MILLIS_IN_TICK; - } - + private void updateMakeInfo(boolean success) { - if (target == player) target.sendPacket(new RecipeItemMakeInfo(recipeList.getId(), target, success)); + if (target == player) target.sendPacket(new RecipeItemMakeInfo(recipeList.getId(), target, + success)); else target.sendPacket(new RecipeShopItemInfo(player.getObjectId(), 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.getCurrentMp()); target.sendPacket(su); } - + private void grabSomeItems() { - int numItems = player.getSkillLevel(172); + int numItems = skillLevel; + while (numItems > 0 && !items.isEmpty()) { - L2ItemInstance item = items.get(0); - - int count = item.getCount(); + TempItem item = items.get(0); + + int count = item.getQuantity(); if (count >= numItems) count = numItems; - - item.setCount(item.getCount() - count); - if (item.getCount() <= 0) items.remove(0); - + + item.setQuantity(item.getQuantity() - count); + if (item.getQuantity() <= 0) items.remove(0); + else items.set(0, item); + numItems -= count; - usedItems += count; - - SystemMessage sm = new SystemMessage(368); // you equipped ... - sm.addNumber(count); - sm.addItemName(item.getItemId()); - if (target == null) player.sendPacket(sm); - else target.sendPacket(sm); + + if (target == player) + { + SystemMessage sm = new SystemMessage(368); // you equipped ... + sm.addNumber(count); + sm.addItemName(item.getItemId()); + player.sendPacket(sm); + } + else target.sendMessage("Manufacturer " + player.getName() + " used " + count + " " + + item.getItemName()); } } - - private boolean validateCraftingEnabled() + + private boolean validateMp() { - if (!Config.IS_CRAFTING_ENABLED) + if (player.getCurrentMp() < manaRequired) { - target.sendMessage("Crafting is currently disabled."); - player.isInCraftMode(false); - activeMakers.remove(player); - updateMakeInfo(false); - return false; - } - return true; - } - - private boolean validateCustomerCanAfford(L2PcInstance customer, L2PcInstance manufacturer) - { - if(customer==manufacturer) return true; - for (L2ManufactureItem temp : manufacturer.getCreateList().getList()) - if (temp.getRecipeId() == recipeList.getId()) // find recipe for item we want manufactured + // rest (wait for MP) + if (Config.ALT_GAME_CREATION) { - int price = temp.getCost(); - if (customer.getAdena() < price) // check price - { - customer.sendPacket(new SystemMessage(SystemMessage.YOU_NOT_ENOUGH_ADENA)); - manufacturer.isInCraftMode(false); - activeMakers.remove(manufacturer); - return false; - } - break; + player.sendPacket(new SetupGauge(0, delay)); + ClientScheduler.scheduleEvent(this, 100 + delay); } - return true; - } - private boolean validateMp() - { - if (player.getCurrentMp() < recipeList.getMpCost()) - { - target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP)); - player.isInCraftMode(false); - activeMakers.remove(player); - updateMakeInfo(false); + else + // no rest - report no mana + { + target.sendPacket(new SystemMessage(SystemMessage.NOT_ENOUGH_MP)); + abort(); // false == don't resume + } return false; } return true; } - - private boolean validateMaterials() + + private List listItems(boolean remove) { L2RecipeInstance[] recipes = recipeList.getRecipes(); - Inventory inv = target.getInventory(); - + List materials = new FastList(); + for (L2RecipeInstance recipe : recipes) { - if (recipe.getQuantity() != 0) + int quantity = recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) + : (int) recipe.getQuantity(); + + if (quantity > 0) { - L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId()); - if ((invItem == null) - || ((recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) - : recipe.getQuantity()) > invItem.getCount())) + L2ItemInstance item = inv.getItemByItemId(recipe.getItemId()); + + // now we verify that we have enough materials + if (item==null || item.getCount() < quantity) { target.sendMessage("You dont have the right elements for making this item" - + ((recipeList.isConsumable() && Config.RATE_CONSUMABLE_COST != 1) ? ".\nDue to server rates you need " - + Config.RATE_CONSUMABLE_COST - + "x more material than listed in recipe" - : "")); - player.isInCraftMode(false); - activeMakers.remove(player); - updateMakeInfo(false); - return false; + + ((recipeList.isConsumable() && Config.RATE_CONSUMABLE_COST != 1) ? ".\nDue to server rates you need " + + Config.RATE_CONSUMABLE_COST + + "x more material than listed in recipe" + : "")); + abort(); + return null; } + + // make new temporary object, just for counting puroses, real item modification only + // on finishCrafting() so we minimize risk of losing items + + TempItem temp = new TempItem(item, quantity); + materials.add(temp); } } - for (L2RecipeInstance recipe : recipes) + if (remove) { - if (recipe.getQuantity() != 0) - { - L2ItemInstance invItem = inv.getItemByItemId(recipe.getItemId()); - invItem = inv.dropItem( - "Manufacture", - invItem.getObjectId(), - recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) - : recipe.getQuantity(), target, - player); - items.add(invItem); - materialsRefPrice += invItem.getReferencePrice() * invItem.getCount(); - totalItems += invItem.getCount(); - } - } - - return true; + _log.warning("item removal requested: "); + for(TempItem tmp : materials) + { + _log.warning("removed "+tmp.getQuantity()+" "+tmp.getItemName()+" from inventory"); + inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), target, + player); + } + } + return materials; } - - private boolean validateSkillLevel() + + private void abort() { - if (recipeList.getLevel() > skillLevel) + updateMakeInfo(false); + player.isInCraftMode(false); + activeMakers.remove(player); + } + + /** + * TODO: This class should be in some other file, but I don't know where + * + * Class explanation: + * Use it for item counting or checking purposes. Any place you don't want to touch 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 int _itemId; + private int _quantity; + private int _ownerId; + private int _referencePrice; + private String _itemName; + + /** + * @param item + * @param quantity of that item + */ + public TempItem(L2ItemInstance item, int quantity) { - player.sendMessage("Need skill level " + recipeList.getLevel()); - player.sendPacket(new ActionFailed()); - player.isInCraftMode(false); - activeMakers.remove(player); - updateMakeInfo(false); - return false; + super(); + _itemId = item.getItemId(); + _quantity = quantity; + _ownerId = item.getOwnerId(); + _itemName = item.getItem().getName(); + _referencePrice = item.getReferencePrice(); } - return true; - } - - private boolean validateRecipeList() - { - if ((recipeList == null) || (recipeList.getRecipes().length == 0)) + + /** + * @return Returns the quantity. + */ + public int getQuantity() { - player.sendMessage("No such recipe"); - player.isInCraftMode(false); - activeMakers.remove(player); - updateMakeInfo(false); - return false; + return _quantity; } - return true; + + /** + * @param quantity The quantity to set. + */ + public void setQuantity(int quantity) + { + _quantity = quantity; + } + + public int 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() { int itemId = recipeList.getItemId(); int itemCount = recipeList.getCount(); - + L2ItemInstance createdItem = target.getInventory().addItem("Manufacture", itemId, itemCount, - target, player); + target, player); + + // inform customer of earned item SystemMessage sm = new SystemMessage(SystemMessage.EARNED_ITEM); sm.addItemName(itemId); sm.addNumber(itemCount); target.sendPacket(sm); - + + if (target != player) + { + // inform manufacturer of earned profit + sm = new SystemMessage(SystemMessage.EARNED_ADENA); + sm.addNumber(price); + player.sendPacket(sm); + } + if (Config.ALT_GAME_CREATION) { int recipeLevel = recipeList.getLevel(); int exp = createdItem.getReferencePrice() * itemCount; // one variation - // exp -= materialsRefPrice; + // exp -= materialsRefPrice; // mat. ref. price is not accurate so other method is better + if (exp < 0) exp = 0; // another variation exp /= recipeLevel; - for (int i = skillLevel; i > recipeLevel; i--) exp /= 4; + for (int i = skillLevel; i > recipeLevel; i--) + exp /= 4; int sp = exp / 10; - player.addExpAndSp((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP, null, - null), - (int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP, null, - null)); - - sm = new SystemMessage(SystemMessage.YOU_EARNED_S1_EXP_AND_S2_SP); - sm.addNumber((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_XP, null, null)); - sm.addNumber((int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_SP, null, null)); - player.sendPacket(sm); + + // FIXME: Added multiplication of Creation speed with XP/SP gain + // slower crafting -> more XP, faster crafting -> less XP + // admins: find balance you like ... you can use ALT_GAME_CREATION_RATE_XP/SP to + // compensate XP/SP (default = 1) + // goal is to make ALT_GAME_CREATION + // a bit slower, bit more interesting, and give some xp/sp but not too much + + 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(L2PcInstance player, int id) { L2RecipeList recipeList = getRecipeList(id - 1); - + if ((recipeList == null) || (recipeList.getRecipes().length == 0)) { player.sendMessage("No recipe for: " + id); @@ -604,8 +768,5 @@ return null; } return recipeList; - }; - - + } } -