Sfoglia il codice sorgente

BETA: Item Enchant Rework:
* Implementing different groups for success rate of different item location slots.
* Implementing different groups for magical weapons.
* Item enchanting is totally retail like now.
* Thanks to: Nos
* Added new Util method to format double values.
* Added new item debugging UI for item enchanting.
* Reviewed by: Zoey76, Nos

Rumen Nikiforov 11 anni fa
parent
commit
153af75d5d
21 ha cambiato i file con 942 aggiunte e 375 eliminazioni
  1. 0 18
      L2J_Server_BETA/dist/game/config/Character.properties
  2. 0 14
      L2J_Server_BETA/java/com/l2jserver/Config.java
  3. 2 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java
  4. 9 19
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/EnchantItemData.java
  5. 166 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/EnchantItemGroupsData.java
  6. 0 123
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/EnchantScroll.java
  7. 1 1
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java
  8. 51 31
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantItem.java
  9. 79 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantItemGroup.java
  10. 93 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantRateItem.java
  11. 29 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantResultType.java
  12. 138 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantScroll.java
  13. 84 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantScrollGroup.java
  14. 60 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/holders/RangeChanceHolder.java
  15. 8 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/items/L2Item.java
  16. 1 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/model/items/L2Weapon.java
  17. 35 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/Debug.java
  18. 171 167
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestEnchantItem.java
  19. 1 1
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestExTryToPutEnchantSupportItem.java
  20. 1 1
      L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestExTryToPutEnchantTargetItem.java
  21. 13 0
      L2J_Server_BETA/java/com/l2jserver/gameserver/util/Util.java

+ 0 - 18
L2J_Server_BETA/dist/game/config/Character.properties

@@ -360,15 +360,6 @@ NpcTalkBlockingTime = 3
 # ---------------------------------------------------------------------------
 # Enchanting
 # ---------------------------------------------------------------------------
-
-# Default enchant rate for scrolls.
-# Be aware the data in enchantData.xml will override those values!
-# This is just default data (For enchants with not defined such variables)
-# Default: 66.66
-EnchantChance = 66.66 
-# Default: 0 (Unlimited)
-MaxEnchantLevel = 0
-
 # This controls the chance an item has to break if it is enchanted.
 # This chance is in %, so if you set this to 100%, enchants will always succeed.
 # DEFAULT NEEDS TO BE VERIFIED, MUST BE CHANGED HERE AND IN CONFIG.JAVA IF NOT CORRECT
@@ -377,15 +368,6 @@ EnchantChanceElementCrystal = 30
 EnchantChanceElementJewel = 20
 EnchantChanceElementEnergy = 10
 
-# This is the maximum safe enchant.
-# Notes:
-#	If EnchantSafeMax is set to for 8 the item will be safely enchanted to 8 regardless of the max enchant value set above.
-#	EnchantSafeMaxFull is for full body armor (upper and lower).
-#	These values should always be greater than 0.
-# Default: 3, 4
-EnchantSafeMax = 3
-EnchantSafeMaxFull = 4
-
 # List of non-enchantable items.
 # Currently apprentice, travelers weapons and Pailaka items
 # Default: 7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,13293,13294,13296

+ 0 - 14
L2J_Server_BETA/java/com/l2jserver/Config.java

@@ -1008,14 +1008,10 @@ public final class Config
 	public static IdFactoryType IDFACTORY_TYPE;
 	public static boolean BAD_ID_CHECKING;
 	
-	public static double ENCHANT_CHANCE;
-	public static int MAX_ENCHANT_LEVEL;
 	public static double ENCHANT_CHANCE_ELEMENT_STONE;
 	public static double ENCHANT_CHANCE_ELEMENT_CRYSTAL;
 	public static double ENCHANT_CHANCE_ELEMENT_JEWEL;
 	public static double ENCHANT_CHANCE_ELEMENT_ENERGY;
-	public static int ENCHANT_SAFE_MAX;
-	public static int ENCHANT_SAFE_MAX_FULL;
 	public static int[] ENCHANT_BLACKLIST;
 	public static int AUGMENTATION_NG_SKILL_CHANCE;
 	public static int AUGMENTATION_NG_GLOW_CHANCE;
@@ -1619,14 +1615,10 @@ public final class Config
 			WAREHOUSE_SLOTS_CLAN = Integer.parseInt(Character.getProperty("MaximumWarehouseSlotsForClan", "150"));
 			ALT_FREIGHT_SLOTS = Integer.parseInt(Character.getProperty("MaximumFreightSlots", "200"));
 			ALT_FREIGHT_PRICE = Integer.parseInt(Character.getProperty("FreightPrice", "1000"));
-			ENCHANT_CHANCE = Double.parseDouble(Character.getProperty("EnchantChance", "66.66"));
-			MAX_ENCHANT_LEVEL = Integer.parseInt(Character.getProperty("MaxEnchantLevel", "0"));
 			ENCHANT_CHANCE_ELEMENT_STONE = Double.parseDouble(Character.getProperty("EnchantChanceElementStone", "50"));
 			ENCHANT_CHANCE_ELEMENT_CRYSTAL = Double.parseDouble(Character.getProperty("EnchantChanceElementCrystal", "30"));
 			ENCHANT_CHANCE_ELEMENT_JEWEL = Double.parseDouble(Character.getProperty("EnchantChanceElementJewel", "20"));
 			ENCHANT_CHANCE_ELEMENT_ENERGY = Double.parseDouble(Character.getProperty("EnchantChanceElementEnergy", "10"));
-			ENCHANT_SAFE_MAX = Integer.parseInt(Character.getProperty("EnchantSafeMax", "3"));
-			ENCHANT_SAFE_MAX_FULL = Integer.parseInt(Character.getProperty("EnchantSafeMaxFull", "4"));
 			String[] notenchantable = Character.getProperty("EnchantBlackList", "7816,7817,7818,7819,7820,7821,7822,7823,7824,7825,7826,7827,7828,7829,7830,7831,13293,13294,13296").split(",");
 			ENCHANT_BLACKLIST = new int[notenchantable.length];
 			for (int i = 0; i < notenchantable.length; i++)
@@ -3497,12 +3489,6 @@ public final class Config
 			case "enchantchanceelementenergy":
 				ENCHANT_CHANCE_ELEMENT_ENERGY = Double.parseDouble(pValue);
 				break;
-			case "enchantsafemax":
-				ENCHANT_SAFE_MAX = Integer.parseInt(pValue);
-				break;
-			case "enchantsafemaxfull":
-				ENCHANT_SAFE_MAX_FULL = Integer.parseInt(pValue);
-				break;
 			case "augmentationngskillchance":
 				AUGMENTATION_NG_SKILL_CHANCE = Integer.parseInt(pValue);
 				break;

+ 2 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/GameServer.java

@@ -52,6 +52,7 @@ import com.l2jserver.gameserver.datatables.DoorTable;
 import com.l2jserver.gameserver.datatables.EnchantGroupsData;
 import com.l2jserver.gameserver.datatables.EnchantHPBonusData;
 import com.l2jserver.gameserver.datatables.EnchantItemData;
+import com.l2jserver.gameserver.datatables.EnchantItemGroupsData;
 import com.l2jserver.gameserver.datatables.EnchantOptionsData;
 import com.l2jserver.gameserver.datatables.EventDroplist;
 import com.l2jserver.gameserver.datatables.ExperienceTable;
@@ -225,6 +226,7 @@ public class GameServer
 		
 		printSection("Items");
 		ItemTable.getInstance();
+		EnchantItemGroupsData.getInstance();
 		EnchantItemData.getInstance();
 		EnchantOptionsData.getInstance();
 		OptionsData.getInstance();

+ 9 - 19
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/EnchantItemData.java

@@ -18,18 +18,16 @@
  */
 package com.l2jserver.gameserver.datatables;
 
-import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
 import org.w3c.dom.NamedNodeMap;
 import org.w3c.dom.Node;
 
 import com.l2jserver.gameserver.engines.DocumentParser;
-import com.l2jserver.gameserver.model.EnchantItem;
-import com.l2jserver.gameserver.model.EnchantScroll;
 import com.l2jserver.gameserver.model.StatsSet;
+import com.l2jserver.gameserver.model.enchant.EnchantItem;
+import com.l2jserver.gameserver.model.enchant.EnchantScroll;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 
 /**
@@ -50,7 +48,7 @@ public class EnchantItemData extends DocumentParser
 	}
 	
 	@Override
-	public void load()
+	public synchronized void load()
 	{
 		_scrolls.clear();
 		_supports.clear();
@@ -64,7 +62,6 @@ public class EnchantItemData extends DocumentParser
 	{
 		StatsSet set;
 		Node att;
-		Map<Integer, Double> enchantSteps;
 		for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
 		{
 			if ("list".equalsIgnoreCase(n.getNodeName()))
@@ -75,27 +72,21 @@ public class EnchantItemData extends DocumentParser
 					{
 						NamedNodeMap attrs = d.getAttributes();
 						set = new StatsSet();
-						enchantSteps = new HashMap<>();
 						for (int i = 0; i < attrs.getLength(); i++)
 						{
 							att = attrs.item(i);
 							set.set(att.getNodeName(), att.getNodeValue());
 						}
 						
-						List<Integer> items = new ArrayList<>();
+						final EnchantScroll item = new EnchantScroll(set);
 						for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
 						{
 							if ("item".equalsIgnoreCase(cd.getNodeName()))
 							{
-								items.add(parseInteger(cd.getAttributes(), "id"));
-							}
-							else if ("step".equalsIgnoreCase(cd.getNodeName()))
-							{
-								enchantSteps.put(parseInt(cd.getAttributes(), "level"), parseDouble(cd.getAttributes(), "successRate"));
+								item.addItem(parseInteger(cd.getAttributes(), "id"));
 							}
 						}
-						EnchantScroll item = new EnchantScroll(set, items, enchantSteps);
-						_scrolls.put(item.getScrollId(), item);
+						_scrolls.put(item.getId(), item);
 					}
 					else if ("support".equalsIgnoreCase(d.getNodeName()))
 					{
@@ -108,16 +99,15 @@ public class EnchantItemData extends DocumentParser
 							set.set(att.getNodeName(), att.getNodeValue());
 						}
 						
-						List<Integer> items = new ArrayList<>();
+						final EnchantItem item = new EnchantItem(set);
 						for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
 						{
 							if ("item".equalsIgnoreCase(cd.getNodeName()))
 							{
-								items.add(parseInteger(cd.getAttributes(), "id"));
+								item.addItem(parseInteger(cd.getAttributes(), "id"));
 							}
 						}
-						EnchantItem item = new EnchantItem(set, items);
-						_supports.put(item.getScrollId(), item);
+						_supports.put(item.getId(), item);
 					}
 				}
 			}

+ 166 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/EnchantItemGroupsData.java

@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.datatables;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+
+import com.l2jserver.gameserver.engines.DocumentParser;
+import com.l2jserver.gameserver.model.enchant.EnchantItemGroup;
+import com.l2jserver.gameserver.model.enchant.EnchantRateItem;
+import com.l2jserver.gameserver.model.enchant.EnchantScrollGroup;
+import com.l2jserver.gameserver.model.holders.RangeChanceHolder;
+import com.l2jserver.gameserver.model.items.L2Item;
+import com.l2jserver.gameserver.util.Util;
+
+/**
+ * @author UnAfraid
+ */
+public final class EnchantItemGroupsData extends DocumentParser
+{
+	private final Map<String, EnchantItemGroup> _itemGroups = new HashMap<>();
+	private final Map<Integer, EnchantScrollGroup> _scrollGroups = new HashMap<>();
+	
+	protected EnchantItemGroupsData()
+	{
+		load();
+	}
+	
+	@Override
+	public synchronized void load()
+	{
+		_itemGroups.clear();
+		_scrollGroups.clear();
+		parseDatapackFile("data/enchantItemGroups.xml");
+		_log.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _itemGroups.size() + " item group templates.");
+		_log.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _scrollGroups.size() + " scroll group templates.");
+	}
+	
+	@Override
+	protected void parseDocument()
+	{
+		for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
+		{
+			if ("list".equalsIgnoreCase(n.getNodeName()))
+			{
+				for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
+				{
+					if ("enchantRateGroup".equalsIgnoreCase(d.getNodeName()))
+					{
+						String name = parseString(d.getAttributes(), "name");
+						final EnchantItemGroup group = new EnchantItemGroup(name);
+						for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
+						{
+							if ("current".equalsIgnoreCase(cd.getNodeName()))
+							{
+								String range = parseString(cd.getAttributes(), "enchant");
+								double chance = parseDouble(cd.getAttributes(), "chance");
+								int min = -1;
+								int max = 0;
+								if (range.contains("-"))
+								{
+									String[] split = range.split("-");
+									if ((split.length == 2) && Util.isDigit(split[0]) && Util.isDigit(split[1]))
+									{
+										min = Integer.parseInt(split[0]);
+										max = Integer.parseInt(split[1]);
+									}
+								}
+								else if (Util.isDigit(range))
+								{
+									min = Integer.parseInt(range);
+									max = min;
+								}
+								if ((min > -1) && (max > 0))
+								{
+									group.addChance(new RangeChanceHolder(min, max, chance));
+								}
+							}
+						}
+						_itemGroups.put(name, group);
+					}
+					else if ("enchantScrollGroup".equals(d.getNodeName()))
+					{
+						int id = parseInt(d.getAttributes(), "id");
+						final EnchantScrollGroup group = new EnchantScrollGroup(id);
+						for (Node cd = d.getFirstChild(); cd != null; cd = cd.getNextSibling())
+						{
+							if ("enchantRate".equalsIgnoreCase(cd.getNodeName()))
+							{
+								final EnchantRateItem rateGroup = new EnchantRateItem(parseString(cd.getAttributes(), "group"));
+								for (Node z = cd.getFirstChild(); z != null; z = z.getNextSibling())
+								{
+									if ("item".equals(z.getNodeName()))
+									{
+										final NamedNodeMap attrs = z.getAttributes();
+										if (attrs.getNamedItem("slot") != null)
+										{
+											rateGroup.addSlot(ItemTable._slots.get(parseString(attrs, "slot")));
+										}
+										else if (attrs.getNamedItem("magicWeapon") != null)
+										{
+											rateGroup.setMagicWeapon(parseBoolean(attrs, "magicWeapon"));
+										}
+										else if (attrs.getNamedItem("id") != null)
+										{
+											rateGroup.setItemId(parseInt(attrs, "id"));
+										}
+									}
+								}
+								group.addRateGroup(rateGroup);
+							}
+						}
+						_scrollGroups.put(id, group);
+					}
+				}
+			}
+		}
+	}
+	
+	public EnchantItemGroup getItemGroup(L2Item item, int scrollGroup)
+	{
+		final EnchantScrollGroup group = _scrollGroups.get(scrollGroup);
+		final EnchantRateItem rateGroup = group.getRateGroup(item);
+		return rateGroup != null ? _itemGroups.get(rateGroup.getName()) : null;
+	}
+	
+	public EnchantItemGroup getItemGroup(String name)
+	{
+		return _itemGroups.get(name);
+	}
+	
+	public EnchantScrollGroup getScrollGroup(int id)
+	{
+		return _scrollGroups.get(id);
+	}
+	
+	public static EnchantItemGroupsData getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	private static class SingletonHolder
+	{
+		protected static final EnchantItemGroupsData _instance = new EnchantItemGroupsData();
+	}
+}

+ 0 - 123
L2J_Server_BETA/java/com/l2jserver/gameserver/model/EnchantScroll.java

@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2004-2013 L2J Server
- * 
- * This file is part of L2J Server.
- * 
- * L2J Server 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.
- * 
- * L2J Server 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.l2jserver.gameserver.model;
-
-import java.util.List;
-import java.util.Map;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jserver.gameserver.model.items.L2Item;
-import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
-
-/**
- * @author UnAfraid
- */
-public class EnchantScroll extends EnchantItem
-{
-	private final boolean _isBlessed;
-	private final boolean _isSafe;
-	private final Map<Integer, Double> _enchantSteps;
-	
-	/**
-	 * @param set
-	 * @param items
-	 * @param enchantSteps
-	 */
-	public EnchantScroll(StatsSet set, List<Integer> items, Map<Integer, Double> enchantSteps)
-	{
-		super(set, items);
-		
-		_isBlessed = set.getBool("isBlessed", false);
-		_isSafe = set.getBool("isSafe", false);
-		_enchantSteps = enchantSteps;
-	}
-	
-	/**
-	 * @return true for blessed scrolls
-	 */
-	public final boolean isBlessed()
-	{
-		return _isBlessed;
-	}
-	
-	/**
-	 * @return true for safe-enchant scrolls (enchant level will remain on failure)
-	 */
-	public final boolean isSafe()
-	{
-		return _isSafe;
-	}
-	
-	/**
-	 * @param enchantItem
-	 * @param supportItem
-	 * @return
-	 */
-	public final boolean isValid(L2ItemInstance enchantItem, EnchantItem supportItem)
-	{
-		// blessed scrolls can't use support items
-		if ((supportItem != null) && (!supportItem.isValid(enchantItem) || isBlessed()))
-		{
-			return false;
-		}
-		
-		return super.isValid(enchantItem);
-	}
-	
-	/**
-	 * @param enchantItem
-	 * @param supportItem
-	 * @return
-	 */
-	public final double getChance(L2ItemInstance enchantItem, EnchantItem supportItem)
-	{
-		if (!isValid(enchantItem, supportItem))
-		{
-			return -1;
-		}
-		
-		boolean fullBody = enchantItem.getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR;
-		if ((enchantItem.getEnchantLevel() < Config.ENCHANT_SAFE_MAX) || (fullBody && (enchantItem.getEnchantLevel() < Config.ENCHANT_SAFE_MAX_FULL)))
-		{
-			return 100;
-		}
-		
-		L2PcInstance activeChar = L2World.getInstance().getPlayer(enchantItem.getOwnerId());
-		int level = enchantItem.getEnchantLevel() + 1;
-		double chance = _chanceAdd;
-		
-		if (_enchantSteps.containsKey(level))
-		{
-			chance = _enchantSteps.get(level);
-		}
-		
-		if (activeChar.isDebug())
-		{
-			activeChar.sendDebugMessage("Enchant Level: " + level + " Chance: " + chance + " " + (supportItem != null ? "Support item: " + supportItem.getChanceAdd() : ""));
-		}
-		
-		if ((supportItem != null) && !_isBlessed)
-		{
-			chance *= supportItem.getChanceAdd();
-		}
-		
-		return chance;
-	}
-}

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/stat/PcStat.java

@@ -654,7 +654,7 @@ public class PcStat extends PlayableStat
 		if (getActiveChar().isMounted())
 		{
 			final L2PetLevelData data = PetDataTable.getInstance().getPetLevelData(getActiveChar().getMountNpcId(), getActiveChar().getMountLevel());
-			float baseSpeed = data != null ? data.getSpeedOnRide(MoveType.RUN) : NpcTable.getInstance().getTemplate(getActiveChar().getMountNpcId()).getBaseMoveSpd(MoveType.RUN);
+			float baseSpeed = data != null ? data.getSpeedOnRide(getActiveChar().isRunning() ? MoveType.RUN : MoveType.WALK) : NpcTable.getInstance().getTemplate(getActiveChar().getMountNpcId()).getBaseMoveSpd(getActiveChar().isRunning() ? MoveType.RUN : MoveType.WALK);
 			return (getMoveSpeed() / baseSpeed);
 		}
 		

+ 51 - 31
L2J_Server_BETA/java/com/l2jserver/gameserver/model/EnchantItem.java → L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantItem.java

@@ -16,12 +16,14 @@
  * 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.l2jserver.gameserver.model;
+package com.l2jserver.gameserver.model.enchant;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.logging.Logger;
 
-import com.l2jserver.Config;
 import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.model.StatsSet;
 import com.l2jserver.gameserver.model.items.L2Item;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 
@@ -30,30 +32,57 @@ import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  */
 public class EnchantItem
 {
-	protected final int _id;
-	protected final boolean _isWeapon;
-	protected final int _grade;
-	protected final int _maxEnchantLevel;
-	protected final double _chanceAdd;
-	protected final List<Integer> _itemIds;
+	protected static final Logger _log = Logger.getLogger(EnchantItem.class.getName());
 	
-	/**
-	 * @param set
-	 * @param items
-	 */
-	public EnchantItem(StatsSet set, List<Integer> items)
+	private final int _id;
+	private final boolean _isWeapon;
+	private final int _grade;
+	private final int _maxEnchantLevel;
+	private final double _bonusRate;
+	private List<Integer> _itemIds;
+	
+	public EnchantItem(StatsSet set)
 	{
 		_id = set.getInteger("id");
 		_isWeapon = set.getBool("isWeapon", true);
 		_grade = ItemTable._crystalTypes.get(set.getString("targetGrade", "none"));
-		_maxEnchantLevel = set.getInteger("maxEnchant", Config.MAX_ENCHANT_LEVEL);
-		_chanceAdd = set.getDouble("successRate", Config.ENCHANT_CHANCE);
-		_itemIds = items;
+		_maxEnchantLevel = set.getInteger("maxEnchant", 65535);
+		_bonusRate = set.getDouble("bonusRate", 0);
+	}
+	
+	/**
+	 * @return id of current item.
+	 */
+	public final int getId()
+	{
+		return _id;
+	}
+	
+	/**
+	 * @return bonus chance that would be added.
+	 */
+	public final double getBonusRate()
+	{
+		return _bonusRate;
+	}
+	
+	public void addItem(int id)
+	{
+		if (_itemIds == null)
+		{
+			_itemIds = new ArrayList<>();
+		}
+		_itemIds.add(id);
+	}
+	
+	public boolean verifyItemId(int itemId)
+	{
+		return _itemIds != null ? _itemIds.contains(itemId) : true;
 	}
 	
 	/**
 	 * @param enchantItem
-	 * @return true if support item can be used for this item
+	 * @return {@code true} if current item is valid to be enchanted, {@code false} otherwise.
 	 */
 	public final boolean isValid(L2ItemInstance enchantItem)
 	{
@@ -77,7 +106,7 @@ public class EnchantItem
 		{
 			return false;
 		}
-		else if (((enchantItem.isEnchantable() > 1) && (_itemIds.isEmpty() || !_itemIds.contains(enchantItem.getItemId()))) || (!_itemIds.isEmpty() && !_itemIds.contains(enchantItem.getItemId())))
+		else if ((enchantItem.isEnchantable() > 1) && !verifyItemId(enchantItem.getItemId()))
 		{
 			return false;
 		}
@@ -85,6 +114,10 @@ public class EnchantItem
 		return true;
 	}
 	
+	/**
+	 * @param type2
+	 * @return {@code true} if current type2 is valid to be enchanted, {@code false} otherwise.
+	 */
 	private boolean isValidItemType(int type2)
 	{
 		if (type2 == L2Item.TYPE2_WEAPON)
@@ -97,17 +130,4 @@ public class EnchantItem
 		}
 		return false;
 	}
-	
-	/**
-	 * @return chance increase
-	 */
-	public final double getChanceAdd()
-	{
-		return _chanceAdd;
-	}
-	
-	public int getScrollId()
-	{
-		return _id;
-	}
 }

+ 79 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantItemGroup.java

@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.enchant;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.l2jserver.gameserver.model.holders.RangeChanceHolder;
+
+/**
+ * @author UnAfraid
+ */
+public final class EnchantItemGroup
+{
+	private static final Logger _log = Logger.getLogger(EnchantItemGroup.class.getName());
+	private final List<RangeChanceHolder> _chances = new ArrayList<>();
+	private final String _name;
+	
+	public EnchantItemGroup(String name)
+	{
+		_name = name;
+	}
+	
+	/**
+	 * @return name of current enchant item group.
+	 */
+	public String getName()
+	{
+		return _name;
+	}
+	
+	/**
+	 * @param holder
+	 */
+	public void addChance(RangeChanceHolder holder)
+	{
+		_chances.add(holder);
+	}
+	
+	/**
+	 * @param index
+	 * @return chance for success rate for current enchant item group.
+	 */
+	public double getChance(int index)
+	{
+		if (!_chances.isEmpty())
+		{
+			for (RangeChanceHolder holder : _chances)
+			{
+				if ((holder.getMin() <= index) && (holder.getMax() >= index))
+				{
+					return holder.getChance();
+				}
+			}
+			_log.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't match proper chance for item group: " + _name, new IllegalStateException());
+			return _chances.get(_chances.size() - 1).getChance();
+		}
+		_log.log(Level.WARNING, getClass().getSimpleName() + ": item group: " + _name + " doesn't have any chances!");
+		return -1;
+	}
+}

+ 93 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantRateItem.java

@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.enchant;
+
+import com.l2jserver.gameserver.model.items.L2Item;
+
+/**
+ * @author UnAfraid
+ */
+public final class EnchantRateItem
+{
+	private final String _name;
+	private int _itemId;
+	private int _slot;
+	private Boolean _isMagicWeapon = null;
+	
+	public EnchantRateItem(String name)
+	{
+		_name = name;
+	}
+	
+	/**
+	 * @return name of enchant group.
+	 */
+	public String getName()
+	{
+		return _name;
+	}
+	
+	/**
+	 * Adds item id verification.
+	 * @param id
+	 */
+	public void setItemId(int id)
+	{
+		_itemId = id;
+	}
+	
+	/**
+	 * Adds body slot verification.
+	 * @param slot
+	 */
+	public void addSlot(int slot)
+	{
+		_slot |= slot;
+	}
+	
+	/**
+	 * Adds magic weapon verification.
+	 * @param magicWeapon
+	 */
+	public void setMagicWeapon(boolean magicWeapon)
+	{
+		_isMagicWeapon = magicWeapon;
+	}
+	
+	/**
+	 * @param item
+	 * @return {@code true} if item can be used with this rate group, {@code false} otherwise.
+	 */
+	public boolean validate(L2Item item)
+	{
+		if ((_itemId != 0) && (_itemId != item.getItemId()))
+		{
+			return false;
+		}
+		else if ((_slot != 0) && ((item.getBodyPart() & _slot) == 0))
+		{
+			return false;
+		}
+		else if ((_isMagicWeapon != null) && (item.isMagicWeapon() != _isMagicWeapon))
+		{
+			return false;
+		}
+		return true;
+	}
+}

+ 29 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantResultType.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.enchant;
+
+/**
+ * @author UnAfraid
+ */
+public enum EnchantResultType
+{
+	ERROR,
+	SUCCESS,
+	FAILURE
+}

+ 138 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantScroll.java

@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.enchant;
+
+import java.util.logging.Level;
+
+import com.l2jserver.gameserver.datatables.EnchantItemGroupsData;
+import com.l2jserver.gameserver.model.StatsSet;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
+import com.l2jserver.gameserver.network.Debug;
+import com.l2jserver.gameserver.util.Util;
+import com.l2jserver.util.Rnd;
+
+/**
+ * @author UnAfraid
+ */
+public final class EnchantScroll extends EnchantItem
+{
+	private final boolean _isBlessed;
+	private final boolean _isSafe;
+	private final int _scrollGroupId;
+	
+	public EnchantScroll(StatsSet set)
+	{
+		super(set);
+		
+		_isBlessed = set.getBool("isBlessed", false);
+		_isSafe = set.getBool("isSafe", false);
+		_scrollGroupId = set.getInteger("scrollGroupId", 0);
+	}
+	
+	/**
+	 * @return {@code true} for blessed scrolls (enchanted item will remain on failure), {@code false} otherwise.
+	 */
+	public boolean isBlessed()
+	{
+		return _isBlessed;
+	}
+	
+	/**
+	 * @return {@code true} for safe-enchant scrolls (enchant level will remain on failure), {@code false} otherwise.
+	 */
+	public boolean isSafe()
+	{
+		return _isSafe;
+	}
+	
+	/**
+	 * @return id of scroll group that should be used.
+	 */
+	public int getScrollGroupId()
+	{
+		return _scrollGroupId;
+	}
+	
+	/**
+	 * @param enchantItem
+	 * @param supportItem
+	 * @return {@code true} if current scroll is valid to be used with support item, {@code false} otherwise.
+	 */
+	public boolean isValid(L2ItemInstance enchantItem, EnchantItem supportItem)
+	{
+		// blessed scrolls can't use support items
+		if ((supportItem != null) && (!supportItem.isValid(enchantItem) || isBlessed()))
+		{
+			return false;
+		}
+		
+		return super.isValid(enchantItem);
+	}
+	
+	/**
+	 * @param player
+	 * @param enchantItem
+	 * @param supportItem
+	 * @return the total chance for success rate of this scroll.
+	 */
+	public EnchantResultType calculateSuccess(L2PcInstance player, L2ItemInstance enchantItem, EnchantItem supportItem)
+	{
+		if (!isValid(enchantItem, supportItem))
+		{
+			return EnchantResultType.FAILURE;
+		}
+		
+		if (EnchantItemGroupsData.getInstance().getScrollGroup(_scrollGroupId) == null)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName() + ": Unexistent enchant scroll group specified for enchant scroll: " + getId());
+			return EnchantResultType.FAILURE;
+		}
+		
+		final EnchantItemGroup group = EnchantItemGroupsData.getInstance().getItemGroup(enchantItem.getItem(), _scrollGroupId);
+		if (group == null)
+		{
+			_log.log(Level.WARNING, getClass().getSimpleName() + ": Couldn't find enchant item group for scroll: " + getId() + " requested by: " + player);
+			return EnchantResultType.FAILURE;
+		}
+		
+		final double chance = group.getChance(enchantItem.getEnchantLevel());
+		final double bonusRate = getBonusRate();
+		final double supportBonusRate = ((supportItem != null) && !_isBlessed) ? supportItem.getBonusRate() : 0;
+		final double finalChance = chance + bonusRate + supportBonusRate;
+		
+		final double random = 100 * Rnd.nextDouble();
+		final boolean success = (random < finalChance);
+		
+		if (player.isDebug())
+		{
+			final StatsSet set = new StatsSet();
+			set.set("chance", Util.formatDouble(chance, "#.##"));
+			set.set("bonusRate", Util.formatDouble(bonusRate, "#.##"));
+			set.set("supportBonusRate", Util.formatDouble(supportBonusRate, "#.##"));
+			set.set("finalChance", Util.formatDouble(finalChance, "#.##"));
+			set.set("random", Util.formatDouble(random, "#.##"));
+			set.set("success", success);
+			set.set("item group", group.getName());
+			set.set("scroll group", _scrollGroupId);
+			Debug.sendItemDebug(player, enchantItem, set);
+		}
+		return success ? EnchantResultType.SUCCESS : EnchantResultType.FAILURE;
+	}
+}

+ 84 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/enchant/EnchantScrollGroup.java

@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.enchant;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.l2jserver.gameserver.model.items.L2Item;
+
+/**
+ * @author UnAfraid
+ */
+public final class EnchantScrollGroup
+{
+	private final int _id;
+	private List<EnchantRateItem> _rateGroups;
+	
+	public EnchantScrollGroup(int id)
+	{
+		_id = id;
+	}
+	
+	/**
+	 * @return id of current enchant scroll group.
+	 */
+	public int getId()
+	{
+		return _id;
+	}
+	
+	/**
+	 * Adds new rate group.
+	 * @param group
+	 */
+	public void addRateGroup(EnchantRateItem group)
+	{
+		if (_rateGroups == null)
+		{
+			_rateGroups = new ArrayList<>();
+		}
+		_rateGroups.add(group);
+	}
+	
+	/**
+	 * @return {@code List} of all enchant rate items, Empty list if none.
+	 */
+	public List<EnchantRateItem> getRateGroups()
+	{
+		return _rateGroups != null ? _rateGroups : Collections.<EnchantRateItem> emptyList();
+	}
+	
+	/**
+	 * @param item
+	 * @return {@link EnchantRateItem}, {@code NULL} in case non of rate items can be used with.
+	 */
+	public EnchantRateItem getRateGroup(L2Item item)
+	{
+		for (EnchantRateItem group : getRateGroups())
+		{
+			if (group.validate(item))
+			{
+				return group;
+			}
+		}
+		return null;
+	}
+}

+ 60 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/holders/RangeChanceHolder.java

@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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.l2jserver.gameserver.model.holders;
+
+/**
+ * @author UnAfraid
+ */
+public class RangeChanceHolder
+{
+	private final int _min;
+	private final int _max;
+	private final double _chance;
+	
+	public RangeChanceHolder(int min, int max, double chance)
+	{
+		_min = min;
+		_max = max;
+		_chance = chance;
+	}
+	
+	/**
+	 * @return minimum value.
+	 */
+	public int getMin()
+	{
+		return _min;
+	}
+	
+	/**
+	 * @return maximum value.
+	 */
+	public int getMax()
+	{
+		return _max;
+	}
+	
+	/**
+	 * @return the chance.
+	 */
+	public double getChance()
+	{
+		return _chance;
+	}
+}

+ 8 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/items/L2Item.java

@@ -405,6 +405,14 @@ public abstract class L2Item
 	 */
 	public abstract L2ItemType getItemType();
 	
+	/**
+	 * @return {@code true} if the weapon is magic, {@code false} otherwise.
+	 */
+	public boolean isMagicWeapon()
+	{
+		return false;
+	}
+	
 	/**
 	 * @return the _equipReuseDelay
 	 */

+ 1 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/items/L2Weapon.java

@@ -206,6 +206,7 @@ public final class L2Weapon extends L2Item
 	/**
 	 * @return {@code true} if the weapon is magic, {@code false} otherwise.
 	 */
+	@Override
 	public boolean isMagicWeapon()
 	{
 		return _isMagicWeapon;

+ 35 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/network/Debug.java

@@ -20,9 +20,12 @@ package com.l2jserver.gameserver.network;
 
 import java.util.Map.Entry;
 
+import com.l2jserver.gameserver.datatables.ItemTable;
 import com.l2jserver.gameserver.model.Elementals;
 import com.l2jserver.gameserver.model.StatsSet;
 import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 import com.l2jserver.gameserver.model.skills.L2Skill;
 import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
 import com.l2jserver.gameserver.network.serverpackets.TutorialShowHtml;
@@ -75,4 +78,36 @@ public class Debug
 		msg.replace("%details%", sb.toString());
 		attacker.sendPacket(new TutorialShowHtml(msg.getHtml()));
 	}
+	
+	public static void sendItemDebug(L2PcInstance player, L2ItemInstance item, StatsSet set)
+	{
+		final StringBuilder sb = new StringBuilder();
+		for (Entry<String, Object> entry : set.getSet().entrySet())
+		{
+			sb.append("<tr><td>" + entry.getKey() + "</td><td><font color=\"LEVEL\">" + entry.getValue() + "</font></td></tr>");
+		}
+		
+		final NpcHtmlMessage msg = new NpcHtmlMessage(0);
+		msg.setFile(player.getHtmlPrefix(), "data/html/admin/itemdebug.htm");
+		msg.replace("%itemName%", item.getName());
+		msg.replace("%itemSlot%", getBodyPart(item.getItem().getBodyPart()));
+		msg.replace("%itemType%", item.isArmor() ? "Armor" : item.isWeapon() ? "Weapon" : "Etc");
+		msg.replace("%enchantLevel%", item.getEnchantLevel());
+		msg.replace("%isMagicWeapon%", item.getItem().isMagicWeapon());
+		msg.replace("%item%", item.toString());
+		msg.replace("%details%", sb.toString());
+		player.sendPacket(new TutorialShowHtml(msg.getHtml()));
+	}
+	
+	private static String getBodyPart(int bodyPart)
+	{
+		for (Entry<String, Integer> entry : ItemTable._slots.entrySet())
+		{
+			if ((entry.getValue() & bodyPart) == bodyPart)
+			{
+				return entry.getKey();
+			}
+		}
+		return "Unknown";
+	}
 }

+ 171 - 167
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestEnchantItem.java

@@ -25,10 +25,11 @@ import java.util.logging.Logger;
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.datatables.EnchantItemData;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.model.EnchantItem;
-import com.l2jserver.gameserver.model.EnchantScroll;
 import com.l2jserver.gameserver.model.L2World;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.enchant.EnchantItem;
+import com.l2jserver.gameserver.model.enchant.EnchantResultType;
+import com.l2jserver.gameserver.model.enchant.EnchantScroll;
 import com.l2jserver.gameserver.model.items.L2Armor;
 import com.l2jserver.gameserver.model.items.L2Item;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
@@ -41,7 +42,6 @@ import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
 import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.util.Util;
-import com.l2jserver.util.Rnd;
 
 public final class RequestEnchantItem extends L2GameClientPacket
 {
@@ -158,143 +158,94 @@ public final class RequestEnchantItem extends L2GameClientPacket
 		
 		synchronized (item)
 		{
-			double chance = scrollTemplate.getChance(item, supportTemplate);
-			
-			L2Skill enchant4Skill = null;
-			L2Item it = item.getItem();
-			
 			// last validation check
-			if ((item.getOwnerId() != activeChar.getObjectId()) || (item.isEnchantable() == 0) || (chance < 0))
+			if ((item.getOwnerId() != activeChar.getObjectId()) || (item.isEnchantable() == 0))
 			{
 				activeChar.sendPacket(SystemMessageId.INAPPROPRIATE_ENCHANT_CONDITION);
 				activeChar.setActiveEnchantItemId(L2PcInstance.ID_NONE);
 				activeChar.sendPacket(new EnchantResult(2, 0, 0));
 				return;
 			}
-			
-			if (Rnd.get(100) < chance)
+			final EnchantResultType resultType = scrollTemplate.calculateSuccess(activeChar, item, supportTemplate);
+			switch (resultType)
 			{
-				// success
-				item.setEnchantLevel(item.getEnchantLevel() + 1);
-				item.updateDatabase();
-				activeChar.sendPacket(new EnchantResult(0, 0, 0));
-				
-				if (Config.LOG_ITEM_ENCHANTS)
+				case ERROR:
 				{
-					LogRecord record = new LogRecord(Level.INFO, "Success");
-					record.setParameters(new Object[]
-					{
-						activeChar,
-						item,
-						scroll,
-						support,
-						chance
-					});
-					record.setLoggerName("item");
-					_logEnchant.log(record);
+					activeChar.sendPacket(SystemMessageId.INAPPROPRIATE_ENCHANT_CONDITION);
+					activeChar.setActiveEnchantItemId(L2PcInstance.ID_NONE);
+					activeChar.sendPacket(new EnchantResult(2, 0, 0));
+					break;
 				}
-				
-				// announce the success
-				int minEnchantAnnounce = item.isArmor() ? 6 : 7;
-				int maxEnchantAnnounce = item.isArmor() ? 0 : 15;
-				if ((item.getEnchantLevel() == minEnchantAnnounce) || (item.getEnchantLevel() == maxEnchantAnnounce))
+				case SUCCESS:
 				{
-					SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_SUCCESSFULY_ENCHANTED_A_S2_S3);
-					sm.addCharName(activeChar);
-					sm.addNumber(item.getEnchantLevel());
-					sm.addItemName(item);
-					activeChar.broadcastPacket(sm);
+					L2Skill enchant4Skill = null;
+					L2Item it = item.getItem();
 					
-					L2Skill skill = SkillTable.FrequentSkill.FIREWORK.getSkill();
-					if (skill != null)
-					{
-						activeChar.broadcastPacket(new MagicSkillUse(activeChar, activeChar, skill.getId(), skill.getLevel(), skill.getHitTime(), skill.getReuseDelay()));
-					}
-				}
-				
-				if ((it instanceof L2Armor) && (item.getEnchantLevel() == 4) && activeChar.getInventory().getItemByObjectId(item.getObjectId()).isEquipped())
-				{
-					enchant4Skill = ((L2Armor) it).getEnchant4Skill();
-					if (enchant4Skill != null)
-					{
-						// add skills bestowed from +4 armor
-						activeChar.addSkill(enchant4Skill, false);
-						activeChar.sendSkillList();
-					}
-				}
-			}
-			else
-			{
-				if (scrollTemplate.isSafe())
-				{
-					// safe enchant - remain old value
-					activeChar.sendPacket(SystemMessageId.SAFE_ENCHANT_FAILED);
-					activeChar.sendPacket(new EnchantResult(5, 0, 0));
+					item.setEnchantLevel(item.getEnchantLevel() + 1);
+					item.updateDatabase();
+					activeChar.sendPacket(new EnchantResult(0, 0, 0));
 					
 					if (Config.LOG_ITEM_ENCHANTS)
 					{
-						LogRecord record = new LogRecord(Level.INFO, "Safe Fail");
+						LogRecord record = new LogRecord(Level.INFO, "Success");
 						record.setParameters(new Object[]
 						{
 							activeChar,
 							item,
 							scroll,
 							support,
-							chance
 						});
 						record.setLoggerName("item");
 						_logEnchant.log(record);
 					}
-				}
-				else
-				{
-					// unequip item on enchant failure to avoid item skills stack
-					if (item.isEquipped())
+					
+					// announce the success
+					int minEnchantAnnounce = item.isArmor() ? 6 : 7;
+					int maxEnchantAnnounce = item.isArmor() ? 0 : 15;
+					if ((item.getEnchantLevel() == minEnchantAnnounce) || (item.getEnchantLevel() == maxEnchantAnnounce))
 					{
-						if (item.getEnchantLevel() > 0)
-						{
-							SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
-							sm.addNumber(item.getEnchantLevel());
-							sm.addItemName(item);
-							activeChar.sendPacket(sm);
-						}
-						else
-						{
-							SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED);
-							sm.addItemName(item);
-							activeChar.sendPacket(sm);
-						}
+						SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_SUCCESSFULY_ENCHANTED_A_S2_S3);
+						sm.addCharName(activeChar);
+						sm.addNumber(item.getEnchantLevel());
+						sm.addItemName(item);
+						activeChar.broadcastPacket(sm);
 						
-						L2ItemInstance[] unequiped = activeChar.getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot());
-						InventoryUpdate iu = new InventoryUpdate();
-						for (L2ItemInstance itm : unequiped)
+						L2Skill skill = SkillTable.FrequentSkill.FIREWORK.getSkill();
+						if (skill != null)
 						{
-							iu.addModifiedItem(itm);
+							activeChar.broadcastPacket(new MagicSkillUse(activeChar, activeChar, skill.getId(), skill.getLevel(), skill.getHitTime(), skill.getReuseDelay()));
 						}
-						
-						activeChar.sendPacket(iu);
-						activeChar.broadcastUserInfo();
 					}
 					
-					if (scrollTemplate.isBlessed())
+					if ((it instanceof L2Armor) && (item.getEnchantLevel() == 4) && activeChar.getInventory().getItemByObjectId(item.getObjectId()).isEquipped())
 					{
-						// blessed enchant - clear enchant value
-						activeChar.sendPacket(SystemMessageId.BLESSED_ENCHANT_FAILED);
-						
-						item.setEnchantLevel(0);
-						item.updateDatabase();
-						activeChar.sendPacket(new EnchantResult(3, 0, 0));
+						enchant4Skill = ((L2Armor) it).getEnchant4Skill();
+						if (enchant4Skill != null)
+						{
+							// add skills bestowed from +4 armor
+							activeChar.addSkill(enchant4Skill, false);
+							activeChar.sendSkillList();
+						}
+					}
+					break;
+				}
+				case FAILURE:
+				{
+					if (scrollTemplate.isSafe())
+					{
+						// safe enchant - remain old value
+						activeChar.sendPacket(SystemMessageId.SAFE_ENCHANT_FAILED);
+						activeChar.sendPacket(new EnchantResult(5, 0, 0));
 						
 						if (Config.LOG_ITEM_ENCHANTS)
 						{
-							LogRecord record = new LogRecord(Level.INFO, "Blessed Fail");
+							LogRecord record = new LogRecord(Level.INFO, "Safe Fail");
 							record.setParameters(new Object[]
 							{
 								activeChar,
 								item,
 								scroll,
 								support,
-								chance
 							});
 							record.setLoggerName("item");
 							_logEnchant.log(record);
@@ -302,117 +253,170 @@ public final class RequestEnchantItem extends L2GameClientPacket
 					}
 					else
 					{
-						// enchant failed, destroy item
-						int crystalId = item.getItem().getCrystalItemId();
-						int count = item.getCrystalCount() - ((item.getItem().getCrystalCount() + 1) / 2);
-						if (count < 1)
+						// unequip item on enchant failure to avoid item skills stack
+						if (item.isEquipped())
 						{
-							count = 1;
+							if (item.getEnchantLevel() > 0)
+							{
+								SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
+								sm.addNumber(item.getEnchantLevel());
+								sm.addItemName(item);
+								activeChar.sendPacket(sm);
+							}
+							else
+							{
+								SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED);
+								sm.addItemName(item);
+								activeChar.sendPacket(sm);
+							}
+							
+							L2ItemInstance[] unequiped = activeChar.getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot());
+							InventoryUpdate iu = new InventoryUpdate();
+							for (L2ItemInstance itm : unequiped)
+							{
+								iu.addModifiedItem(itm);
+							}
+							
+							activeChar.sendPacket(iu);
+							activeChar.broadcastUserInfo();
 						}
 						
-						L2ItemInstance destroyItem = activeChar.getInventory().destroyItem("Enchant", item, activeChar, null);
-						if (destroyItem == null)
+						if (scrollTemplate.isBlessed())
 						{
-							// unable to destroy item, cheater ?
-							Util.handleIllegalPlayerAction(activeChar, "Unable to delete item on enchant failure from player " + activeChar.getName() + ", possible cheater !", Config.DEFAULT_PUNISH);
-							activeChar.setActiveEnchantItemId(L2PcInstance.ID_NONE);
-							activeChar.sendPacket(new EnchantResult(2, 0, 0));
+							// blessed enchant - clear enchant value
+							activeChar.sendPacket(SystemMessageId.BLESSED_ENCHANT_FAILED);
+							
+							item.setEnchantLevel(0);
+							item.updateDatabase();
+							activeChar.sendPacket(new EnchantResult(3, 0, 0));
 							
 							if (Config.LOG_ITEM_ENCHANTS)
 							{
-								LogRecord record = new LogRecord(Level.INFO, "Unable to destroy");
+								LogRecord record = new LogRecord(Level.INFO, "Blessed Fail");
 								record.setParameters(new Object[]
 								{
 									activeChar,
 									item,
 									scroll,
 									support,
-									chance
 								});
 								record.setLoggerName("item");
 								_logEnchant.log(record);
 							}
-							return;
 						}
-						
-						L2ItemInstance crystals = null;
-						if (crystalId != 0)
+						else
 						{
-							crystals = activeChar.getInventory().addItem("Enchant", crystalId, count, activeChar, destroyItem);
+							// enchant failed, destroy item
+							int crystalId = item.getItem().getCrystalItemId();
+							int count = item.getCrystalCount() - ((item.getItem().getCrystalCount() + 1) / 2);
+							if (count < 1)
+							{
+								count = 1;
+							}
 							
-							SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
-							sm.addItemName(crystals);
-							sm.addItemNumber(count);
-							activeChar.sendPacket(sm);
-						}
-						
-						if (!Config.FORCE_INVENTORY_UPDATE)
-						{
-							InventoryUpdate iu = new InventoryUpdate();
-							if (destroyItem.getCount() == 0)
+							L2ItemInstance destroyItem = activeChar.getInventory().destroyItem("Enchant", item, activeChar, null);
+							if (destroyItem == null)
 							{
-								iu.addRemovedItem(destroyItem);
+								// unable to destroy item, cheater ?
+								Util.handleIllegalPlayerAction(activeChar, "Unable to delete item on enchant failure from player " + activeChar.getName() + ", possible cheater !", Config.DEFAULT_PUNISH);
+								activeChar.setActiveEnchantItemId(L2PcInstance.ID_NONE);
+								activeChar.sendPacket(new EnchantResult(2, 0, 0));
+								
+								if (Config.LOG_ITEM_ENCHANTS)
+								{
+									LogRecord record = new LogRecord(Level.INFO, "Unable to destroy");
+									record.setParameters(new Object[]
+									{
+										activeChar,
+										item,
+										scroll,
+										support,
+									});
+									record.setLoggerName("item");
+									_logEnchant.log(record);
+								}
+								return;
 							}
-							else
+							
+							L2ItemInstance crystals = null;
+							if (crystalId != 0)
 							{
-								iu.addModifiedItem(destroyItem);
+								crystals = activeChar.getInventory().addItem("Enchant", crystalId, count, activeChar, destroyItem);
+								
+								SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
+								sm.addItemName(crystals);
+								sm.addItemNumber(count);
+								activeChar.sendPacket(sm);
 							}
 							
-							if (crystals != null)
+							if (!Config.FORCE_INVENTORY_UPDATE)
+							{
+								InventoryUpdate iu = new InventoryUpdate();
+								if (destroyItem.getCount() == 0)
+								{
+									iu.addRemovedItem(destroyItem);
+								}
+								else
+								{
+									iu.addModifiedItem(destroyItem);
+								}
+								
+								if (crystals != null)
+								{
+									iu.addItem(crystals);
+								}
+								
+								if (scroll.getCount() == 0)
+								{
+									iu.addRemovedItem(scroll);
+								}
+								else
+								{
+									iu.addModifiedItem(scroll);
+								}
+								
+								activeChar.sendPacket(iu);
+							}
+							else
 							{
-								iu.addItem(crystals);
+								activeChar.sendPacket(new ItemList(activeChar, true));
 							}
 							
-							if (scroll.getCount() == 0)
+							L2World world = L2World.getInstance();
+							world.removeObject(destroyItem);
+							if (crystalId == 0)
 							{
-								iu.addRemovedItem(scroll);
+								activeChar.sendPacket(new EnchantResult(4, 0, 0));
 							}
 							else
 							{
-								iu.addModifiedItem(scroll);
+								activeChar.sendPacket(new EnchantResult(1, crystalId, count));
 							}
 							
-							activeChar.sendPacket(iu);
-						}
-						else
-						{
-							activeChar.sendPacket(new ItemList(activeChar, true));
-						}
-						
-						L2World world = L2World.getInstance();
-						world.removeObject(destroyItem);
-						if (crystalId == 0)
-						{
-							activeChar.sendPacket(new EnchantResult(4, 0, 0));
-						}
-						else
-						{
-							activeChar.sendPacket(new EnchantResult(1, crystalId, count));
-						}
-						
-						if (Config.LOG_ITEM_ENCHANTS)
-						{
-							LogRecord record = new LogRecord(Level.INFO, "Fail");
-							record.setParameters(new Object[]
+							if (Config.LOG_ITEM_ENCHANTS)
 							{
-								activeChar,
-								item,
-								scroll,
-								support,
-								chance
-							});
-							record.setLoggerName("item");
-							_logEnchant.log(record);
+								LogRecord record = new LogRecord(Level.INFO, "Fail");
+								record.setParameters(new Object[]
+								{
+									activeChar,
+									item,
+									scroll,
+									support,
+								});
+								record.setLoggerName("item");
+								_logEnchant.log(record);
+							}
 						}
 					}
+					break;
 				}
 			}
 			
-			StatusUpdate su = new StatusUpdate(activeChar);
+			final StatusUpdate su = new StatusUpdate(activeChar);
 			su.addAttribute(StatusUpdate.CUR_LOAD, activeChar.getCurrentLoad());
 			activeChar.sendPacket(su);
 			
-			InventoryUpdate iu = new InventoryUpdate();
+			final InventoryUpdate iu = new InventoryUpdate();
 			if (scroll.getCount() == 0)
 			{
 				iu.addRemovedItem(scroll);

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestExTryToPutEnchantSupportItem.java

@@ -19,8 +19,8 @@
 package com.l2jserver.gameserver.network.clientpackets;
 
 import com.l2jserver.gameserver.datatables.EnchantItemData;
-import com.l2jserver.gameserver.model.EnchantItem;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.enchant.EnchantItem;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.ExPutEnchantSupportItemResult;

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/network/clientpackets/RequestExTryToPutEnchantTargetItem.java

@@ -21,8 +21,8 @@ package com.l2jserver.gameserver.network.clientpackets;
 import java.util.logging.Level;
 
 import com.l2jserver.gameserver.datatables.EnchantItemData;
-import com.l2jserver.gameserver.model.EnchantScroll;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.enchant.EnchantScroll;
 import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
 import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.ExPutEnchantTargetItemResult;

+ 13 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/util/Util.java

@@ -20,6 +20,8 @@ package com.l2jserver.gameserver.util;
 
 import java.io.File;
 import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
 import java.text.NumberFormat;
 import java.text.SimpleDateFormat;
 import java.util.Collection;
@@ -424,6 +426,17 @@ public final class Util
 		}
 	}
 	
+	/**
+	 * @param val
+	 * @param format
+	 * @return formatted double value by specified format.
+	 */
+	public static String formatDouble(double val, String format)
+	{
+		final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH));
+		return formatter.format(val);
+	}
+	
 	/**
 	 * Format the given date on the given format
 	 * @param date : the date to format.