diff --git a/L2J_Mobius_06.0_Fafurion/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_06.0_Fafurion/dist/game/data/xsd/CrystallizableItems.xsd index 3cf5d3603d..d99c46db70 100644 --- a/L2J_Mobius_06.0_Fafurion/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_06.0_Fafurion/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 58eaf3e3a0..d0f61b522c 100644 --- a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/xsd/CrystallizableItems.xsd index 3cf5d3603d..d99c46db70 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 119ad7658f..7702447797 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_08.2_Homunculus/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_08.2_Homunculus/dist/game/data/xsd/CrystallizableItems.xsd index 3cf5d3603d..d99c46db70 100644 --- a/L2J_Mobius_08.2_Homunculus/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_08.2_Homunculus/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index e66d7c6b99..18d4c72d97 100644 --- a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/xsd/CrystallizableItems.xsd index ea971798bc..b2b973f42c 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index e66d7c6b99..18d4c72d97 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_10.2_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_10.2_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd index ea971798bc..b2b973f42c 100644 --- a/L2J_Mobius_10.2_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_10.2_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java index 2f5ebbd355..68676a6bbc 100644 --- a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java +++ b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java @@ -22,10 +22,12 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -451,6 +453,16 @@ public class RequestEnchantItem implements ClientPacket } } + if (resultType == EnchantResultType.FAILURE) + { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + } + player.sendItemList(); player.broadcastUserInfo(); diff --git a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java index 3e9303fe95..82802efc97 100644 --- a/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java +++ b/L2J_Mobius_10.2_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java @@ -24,9 +24,11 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -307,6 +309,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket _result.put(i, "FAIL"); } + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, enchantItem); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + _failureReward.put(_failureReward.size() + 1, destroyReward); + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_10.3_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_10.3_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd index ea971798bc..b2b973f42c 100644 --- a/L2J_Mobius_10.3_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_10.3_MasterClass/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java index d34143db5b..8db6e32f87 100644 --- a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java +++ b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java @@ -22,10 +22,12 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -451,6 +453,16 @@ public class RequestEnchantItem implements ClientPacket } } + if (resultType == EnchantResultType.FAILURE) + { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + } + player.sendItemList(); player.broadcastUserInfo(); diff --git a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java index 3e9303fe95..82802efc97 100644 --- a/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java +++ b/L2J_Mobius_10.3_MasterClass/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java @@ -24,9 +24,11 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -307,6 +309,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket _result.put(i, "FAIL"); } + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, enchantItem); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + _failureReward.put(_failureReward.size() + 1, destroyReward); + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Classic_2.9.5_Saviors/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index fddb9cee55..09df5fcf97 100644 --- a/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_Classic_2.9.5_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Classic_2.9_SecretOfEmpire/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 81c93d5499..528a82f902 100644 --- a/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_Classic_2.9_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 81c93d5499..528a82f902 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count)); } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 4a8d4bbef2..7fd3a50db7 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -358,6 +360,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -391,6 +395,12 @@ public class RequestEnchantItem implements ClientPacket return; } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + World.getInstance().removeObject(item); int count = 0; diff --git a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java index 3488a5ec79..102469a7f9 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/network/clientpackets/RequestEnchantItem.java @@ -22,11 +22,13 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.enums.UserInfoType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -358,6 +360,8 @@ public class RequestEnchantItem implements ClientPacket } else { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + // enchant failed, destroy item if (player.getInventory().destroyItem("Enchant", item, player, null) == null) { @@ -391,6 +395,12 @@ public class RequestEnchantItem implements ClientPacket return; } + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + World.getInstance().removeObject(item); int count = 0; diff --git a/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Essence_6.2_Vanguard/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java index 77f2e5363e..20200e8d0c 100644 --- a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java +++ b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java @@ -22,10 +22,12 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -457,6 +459,16 @@ public class RequestEnchantItem implements ClientPacket } } + if (resultType == EnchantResultType.FAILURE) + { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + } + player.sendItemList(); player.broadcastUserInfo(); diff --git a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java index a866456ca1..b3f48dc2f1 100644 --- a/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java +++ b/L2J_Mobius_Essence_6.2_Vanguard/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java @@ -24,9 +24,11 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -313,6 +315,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket _result.put(i, "FAIL"); } + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, enchantItem); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + _failureReward.put(_failureReward.size() + 1, destroyReward); + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder(); diff --git a/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/CrystallizableItems.xml b/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/CrystallizableItems.xml index bd6d33e5e5..fdf9678fd8 100644 --- a/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/CrystallizableItems.xml +++ b/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/CrystallizableItems.xml @@ -1,4 +1,30 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/xsd/CrystallizableItems.xsd b/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/xsd/CrystallizableItems.xsd index 5be12d71cd..d7810a2113 100644 --- a/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/xsd/CrystallizableItems.xsd +++ b/L2J_Mobius_Essence_6.3_Crusader/dist/game/data/xsd/CrystallizableItems.xsd @@ -43,6 +43,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java index 8608f2725a..1b3fb84318 100644 --- a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java +++ b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/data/xml/ItemCrystallizationData.java @@ -31,16 +31,19 @@ import org.w3c.dom.Node; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.gameserver.data.ItemTable; import org.l2jmobius.gameserver.enums.CrystallizationType; +import org.l2jmobius.gameserver.model.StatSet; +import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder; import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.Armor; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.Weapon; +import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure; import org.l2jmobius.gameserver.model.item.instance.Item; import org.l2jmobius.gameserver.model.item.type.CrystalType; /** - * @author UnAfraid + * @author UnAfraid, Index */ public class ItemCrystallizationData implements IXmlReader { @@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader private final Map>> _crystallizationTemplates = new EnumMap<>(CrystalType.class); private final Map _items = new HashMap<>(); + private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure(); + private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure(); + protected ItemCrystallizationData() { load(); @@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader _crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class)); } _items.clear(); + + _weaponDestroyGroup = new RewardItemsOnFailure(); + _armorDestroyGroup = new RewardItemsOnFailure(); + parseDatapackFile("data/CrystallizableItems.xml"); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); - LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + + if (_crystallizationTemplates.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates."); + } + if (_items.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items."); + } // Generate remaining data. generateCrystallizationData(); + + if (_weaponDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards."); + } + if (_armorDestroyGroup.size() > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards."); + } } @Override @@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader } } } + else if ("itemsOnEnchantFailure".equals(o.getNodeName())) + { + for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling()) + { + if ("armor".equalsIgnoreCase(d.getNodeName())) + { + _armorDestroyGroup = getFormedHolder(d); + } + else if ("weapon".equalsIgnoreCase(d.getNodeName())) + { + _weaponDestroyGroup = getFormedHolder(d); + } + } + } } } } @@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader } } - LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates."); + final int generated = _items.size() - previousCount; + if (generated > 0) + { + LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates."); + } } public List getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType) @@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader return result; } + private RewardItemsOnFailure getFormedHolder(Node node) + { + final RewardItemsOnFailure holder = new RewardItemsOnFailure(); + for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling()) + { + if ("item".equals(z.getNodeName())) + { + final StatSet failItems = new StatSet(parseAttributes(z)); + final int itemId = failItems.getInt("id"); + final int enchantLevel = failItems.getInt("enchant"); + final double chance = failItems.getDouble("chance"); + for (CrystalType grade : CrystalType.values()) + { + final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE); + if (count == Integer.MIN_VALUE) + { + continue; + } + + holder.addItemToHolder(itemId, grade, enchantLevel, count, chance); + } + } + } + return holder; + } + + public ItemChanceHolder getItemOnDestroy(Player player, Item item) + { + if ((player == null) || (item == null)) + { + return null; + } + + final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup; + final CrystalType grade = item.getTemplate().getCrystalTypePlus(); + if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel())) + { + return null; + } + + return holder.getRewardItem(grade, item.getEnchantLevel()); + } + /** * Gets the single instance of ItemCrystalizationData. * @return single instance of ItemCrystalizationData diff --git a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java new file mode 100644 index 0000000000..dbba547e54 --- /dev/null +++ b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/model/item/enchant/RewardItemsOnFailure.java @@ -0,0 +1,82 @@ +/* + * This file is part of the L2J Mobius project. + * + * 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 . + */ +package org.l2jmobius.gameserver.model.item.enchant; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; +import org.l2jmobius.gameserver.model.item.type.CrystalType; + +/** + * @author Index + */ +public class RewardItemsOnFailure +{ + private final Map> _rewards = new EnumMap<>(CrystalType.class); + private int _minEnchantLevel = Integer.MAX_VALUE; + private int _maxEnchantLevel = Integer.MIN_VALUE; + + public RewardItemsOnFailure() + { + } + + public void addItemToHolder(int itemId, CrystalType grade, int enchantLevel, long count, double chance) + { + final ItemChanceHolder item = new ItemChanceHolder(itemId, chance, count); + _rewards.computeIfAbsent(grade, v -> new HashMap<>()).put(enchantLevel, item); + _minEnchantLevel = Math.min(_minEnchantLevel, enchantLevel); + _maxEnchantLevel = Math.max(_maxEnchantLevel, enchantLevel); + } + + public ItemChanceHolder getRewardItem(CrystalType grade, int enchantLevel) + { + return _rewards.getOrDefault(grade, new HashMap<>()).getOrDefault(enchantLevel, null); + } + + public boolean checkIfRewardUnavailable(CrystalType grade, int enchantLevel) + { + // reversed available + if (_minEnchantLevel > enchantLevel) + { + return true; + } + + if (_maxEnchantLevel < enchantLevel) + { + return true; + } + + if (!_rewards.containsKey(grade)) + { + return true; + } + + return !_rewards.get(grade).containsKey(enchantLevel); + } + + public int size() + { + int count = 0; + for (Map rewards : _rewards.values()) + { + count += rewards.size(); + } + return count; + } +} diff --git a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java index 97dba47801..9952f650b1 100644 --- a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java +++ b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/RequestEnchantItem.java @@ -22,10 +22,12 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.enums.ItemSkillType; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.item.ItemTemplate; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -457,6 +459,16 @@ public class RequestEnchantItem implements ClientPacket } } + if (resultType == EnchantResultType.FAILURE) + { + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + } + player.sendItemList(); player.broadcastUserInfo(); diff --git a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java index a866456ca1..b3f48dc2f1 100644 --- a/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java +++ b/L2J_Mobius_Essence_6.3_Crusader/java/org/l2jmobius/gameserver/network/clientpackets/enchant/multi/ExRequestMultiEnchantItemList.java @@ -24,9 +24,11 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.network.ReadablePacket; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.data.xml.EnchantItemData; +import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.actor.Player; import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest; +import org.l2jmobius.gameserver.model.holders.ItemChanceHolder; import org.l2jmobius.gameserver.model.holders.ItemHolder; import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType; import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll; @@ -313,6 +315,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket _result.put(i, "FAIL"); } + final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, enchantItem); + if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance())) + { + _failureReward.put(_failureReward.size() + 1, destroyReward); + player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true); + player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount())); + } + if (Config.LOG_ITEM_ENCHANTS) { final StringBuilder sb = new StringBuilder();