Addition of rewards on enchant failure.
Contributed by Index.
This commit is contained in:
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,10 +22,12 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
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.sendItemList();
|
||||||
player.broadcastUserInfo();
|
player.broadcastUserInfo();
|
||||||
|
|
||||||
|
@@ -24,9 +24,11 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.holders.ItemHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -307,6 +309,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket
|
|||||||
_result.put(i, "FAIL");
|
_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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,10 +22,12 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
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.sendItemList();
|
||||||
player.broadcastUserInfo();
|
player.broadcastUserInfo();
|
||||||
|
|
||||||
|
@@ -24,9 +24,11 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.holders.ItemHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -307,6 +309,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket
|
|||||||
_result.put(i, "FAIL");
|
_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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
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));
|
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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -358,6 +360,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
||||||
{
|
{
|
||||||
@@ -391,6 +395,12 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
return;
|
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);
|
World.getInstance().removeObject(item);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -358,6 +360,8 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
|
||||||
|
|
||||||
// enchant failed, destroy item
|
// enchant failed, destroy item
|
||||||
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
|
||||||
{
|
{
|
||||||
@@ -391,6 +395,12 @@ public class RequestEnchantItem implements ClientPacket
|
|||||||
return;
|
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);
|
World.getInstance().removeObject(item);
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,10 +22,12 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
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.sendItemList();
|
||||||
player.broadcastUserInfo();
|
player.broadcastUserInfo();
|
||||||
|
|
||||||
|
@@ -24,9 +24,11 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.holders.ItemHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -313,6 +315,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket
|
|||||||
_result.put(i, "FAIL");
|
_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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
@@ -1,4 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
<list xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="xsd/CrystallizableItems.xsd">
|
||||||
<!-- Templates for generating automatically crystallization data. Chance is multiplied by the amount of crystals the item template has. -->
|
<!-- Templates for generating automatically crystallization data. -->
|
||||||
|
<itemsOnEnchantFailure>
|
||||||
|
<weapon>
|
||||||
|
<item id="91462" enchant="7" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="15" />
|
||||||
|
<item id="91462" enchant="8" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="18" />
|
||||||
|
<item id="91462" enchant="9" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="21" />
|
||||||
|
<item id="91462" enchant="10" chance="100" amountD="4" amountC="7" amountB="8" amountA="12" amountS="25" />
|
||||||
|
<item id="91462" enchant="11" chance="100" amountD="5" amountC="8" amountB="9" amountA="13" amountS="28" />
|
||||||
|
<item id="91462" enchant="12" chance="100" amountD="6" amountC="9" amountB="10" amountA="14" amountS="29" />
|
||||||
|
<item id="91462" enchant="13" chance="100" amountD="7" amountC="12" amountB="13" amountA="19" amountS="30" />
|
||||||
|
<item id="91462" enchant="14" chance="100" amountD="8" amountC="13" amountB="14" amountA="20" amountS="31" />
|
||||||
|
<item id="91462" enchant="15" chance="100" amountD="9" amountC="14" amountB="15" amountA="21" amountS="32" />
|
||||||
|
<item id="91462" enchant="16" chance="100" amountD="10" amountC="17" amountB="18" amountA="26" amountS="33" />
|
||||||
|
<item id="91462" enchant="17" chance="100" amountD="11" amountC="18" amountB="19" amountA="27" amountS="34" />
|
||||||
|
<item id="91462" enchant="18" chance="100" amountD="12" amountC="19" amountB="20" amountA="28" amountS="35" />
|
||||||
|
<item id="91462" enchant="19" chance="100" amountD="14" amountC="25" amountB="28" amountA="35" amountS="36" />
|
||||||
|
</weapon>
|
||||||
|
<armor>
|
||||||
|
<item id="91463" enchant="6" chance="100" amountD="1" amountC="2" amountB="3" amountA="5" amountS="10" />
|
||||||
|
<item id="91463" enchant="7" chance="100" amountD="2" amountC="3" amountB="4" amountA="6" amountS="15" />
|
||||||
|
<item id="91463" enchant="8" chance="100" amountD="3" amountC="4" amountB="5" amountA="7" amountS="20" />
|
||||||
|
<item id="91463" enchant="9" chance="100" amountD="5" amountC="7" amountB="9" amountA="13" amountS="25" />
|
||||||
|
<item id="91463" enchant="10" chance="100" amountD="6" amountC="8" amountB="10" amountA="14" amountS="27"/>
|
||||||
|
<item id="91463" enchant="11" chance="100" amountD="7" amountC="9" amountB="11" amountA="16" amountS="28" />
|
||||||
|
<item id="91463" enchant="12" chance="100" amountD="12" amountC="15" amountB="19" amountA="26" amountS="30" />
|
||||||
|
</armor>
|
||||||
|
</itemsOnEnchantFailure>
|
||||||
</list>
|
</list>
|
@@ -43,6 +43,48 @@
|
|||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
<xs:element name="itemsOnEnchantFailure" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="weapon" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
<xs:element name="armor" maxOccurs="1" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:sequence>
|
||||||
|
<xs:element name="item" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xs:complexType>
|
||||||
|
<xs:attribute type="xs:int" name="id" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="enchant" use="required" />
|
||||||
|
<xs:attribute type="xs:float" name="chance" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountD" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountC" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountB" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountA" use="required" />
|
||||||
|
<xs:attribute type="xs:int" name="amountS" use="required" />
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
|
</xs:sequence>
|
||||||
|
</xs:complexType>
|
||||||
|
</xs:element>
|
||||||
</xs:sequence>
|
</xs:sequence>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
|
@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
|
|||||||
import org.l2jmobius.commons.util.IXmlReader;
|
import org.l2jmobius.commons.util.IXmlReader;
|
||||||
import org.l2jmobius.gameserver.data.ItemTable;
|
import org.l2jmobius.gameserver.data.ItemTable;
|
||||||
import org.l2jmobius.gameserver.enums.CrystallizationType;
|
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.CrystallizationDataHolder;
|
||||||
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.Armor;
|
import org.l2jmobius.gameserver.model.item.Armor;
|
||||||
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
import org.l2jmobius.gameserver.model.item.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.Weapon;
|
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.instance.Item;
|
||||||
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
import org.l2jmobius.gameserver.model.item.type.CrystalType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author UnAfraid
|
* @author UnAfraid, Index
|
||||||
*/
|
*/
|
||||||
public class ItemCrystallizationData implements IXmlReader
|
public class ItemCrystallizationData implements IXmlReader
|
||||||
{
|
{
|
||||||
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
|
||||||
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
|
||||||
|
|
||||||
|
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
protected ItemCrystallizationData()
|
protected ItemCrystallizationData()
|
||||||
{
|
{
|
||||||
load();
|
load();
|
||||||
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
|
||||||
}
|
}
|
||||||
_items.clear();
|
_items.clear();
|
||||||
|
|
||||||
|
_weaponDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
_armorDestroyGroup = new RewardItemsOnFailure();
|
||||||
|
|
||||||
parseDatapackFile("data/CrystallizableItems.xml");
|
parseDatapackFile("data/CrystallizableItems.xml");
|
||||||
|
|
||||||
|
if (_crystallizationTemplates.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
|
||||||
|
}
|
||||||
|
if (_items.size() > 0)
|
||||||
|
{
|
||||||
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
|
||||||
|
}
|
||||||
|
|
||||||
// Generate remaining data.
|
// Generate remaining data.
|
||||||
generateCrystallizationData();
|
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
|
@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<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
|
||||||
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
|
|||||||
return result;
|
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.
|
* Gets the single instance of ItemCrystalizationData.
|
||||||
* @return single instance of ItemCrystalizationData
|
* @return single instance of ItemCrystalizationData
|
||||||
|
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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<CrystalType, Map<Integer, ItemChanceHolder>> _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<Integer, ItemChanceHolder> rewards : _rewards.values())
|
||||||
|
{
|
||||||
|
count += rewards.size();
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
@@ -22,10 +22,12 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.ItemSkillType;
|
||||||
import org.l2jmobius.gameserver.model.World;
|
import org.l2jmobius.gameserver.model.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.ItemTemplate;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
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.sendItemList();
|
||||||
player.broadcastUserInfo();
|
player.broadcastUserInfo();
|
||||||
|
|
||||||
|
@@ -24,9 +24,11 @@ import org.l2jmobius.Config;
|
|||||||
import org.l2jmobius.commons.network.ReadablePacket;
|
import org.l2jmobius.commons.network.ReadablePacket;
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
|
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.World;
|
||||||
import org.l2jmobius.gameserver.model.actor.Player;
|
import org.l2jmobius.gameserver.model.actor.Player;
|
||||||
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
|
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.holders.ItemHolder;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
|
||||||
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
|
||||||
@@ -313,6 +315,14 @@ public class ExRequestMultiEnchantItemList implements ClientPacket
|
|||||||
_result.put(i, "FAIL");
|
_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)
|
if (Config.LOG_ITEM_ENCHANTS)
|
||||||
{
|
{
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
Reference in New Issue
Block a user