Addition of rewards on enchant failure.

Contributed by Index.
This commit is contained in:
MobiusDevelopment
2023-01-10 22:18:46 +00:00
parent 3a96d0d0b4
commit a66dc4b72b
63 changed files with 3162 additions and 59 deletions

View File

@@ -31,16 +31,19 @@ import org.w3c.dom.Node;
import org.l2jmobius.commons.util.IXmlReader;
import org.l2jmobius.gameserver.data.ItemTable;
import org.l2jmobius.gameserver.enums.CrystallizationType;
import org.l2jmobius.gameserver.model.StatSet;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.holders.CrystallizationDataHolder;
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import org.l2jmobius.gameserver.model.item.Armor;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.model.item.Weapon;
import org.l2jmobius.gameserver.model.item.enchant.RewardItemsOnFailure;
import org.l2jmobius.gameserver.model.item.instance.Item;
import org.l2jmobius.gameserver.model.item.type.CrystalType;
/**
* @author UnAfraid
* @author UnAfraid, Index
*/
public class ItemCrystallizationData implements IXmlReader
{
@@ -49,6 +52,9 @@ public class ItemCrystallizationData implements IXmlReader
private final Map<CrystalType, Map<CrystallizationType, List<ItemChanceHolder>>> _crystallizationTemplates = new EnumMap<>(CrystalType.class);
private final Map<Integer, CrystallizationDataHolder> _items = new HashMap<>();
private RewardItemsOnFailure _weaponDestroyGroup = new RewardItemsOnFailure();
private RewardItemsOnFailure _armorDestroyGroup = new RewardItemsOnFailure();
protected ItemCrystallizationData()
{
load();
@@ -63,12 +69,32 @@ public class ItemCrystallizationData implements IXmlReader
_crystallizationTemplates.put(crystalType, new EnumMap<>(CrystallizationType.class));
}
_items.clear();
_weaponDestroyGroup = new RewardItemsOnFailure();
_armorDestroyGroup = new RewardItemsOnFailure();
parseDatapackFile("data/CrystallizableItems.xml");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
if (_crystallizationTemplates.size() > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _crystallizationTemplates.size() + " crystallization templates.");
}
if (_items.size() > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _items.size() + " pre-defined crystallizable items.");
}
// Generate remaining data.
generateCrystallizationData();
if (_weaponDestroyGroup.size() > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _weaponDestroyGroup.size() + " weapon enchant failure rewards.");
}
if (_armorDestroyGroup.size() > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _armorDestroyGroup.size() + " armor enchant failure rewards.");
}
}
@Override
@@ -128,6 +154,20 @@ public class ItemCrystallizationData implements IXmlReader
}
}
}
else if ("itemsOnEnchantFailure".equals(o.getNodeName()))
{
for (Node d = o.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("armor".equalsIgnoreCase(d.getNodeName()))
{
_armorDestroyGroup = getFormedHolder(d);
}
else if ("weapon".equalsIgnoreCase(d.getNodeName()))
{
_weaponDestroyGroup = getFormedHolder(d);
}
}
}
}
}
}
@@ -179,7 +219,11 @@ public class ItemCrystallizationData implements IXmlReader
}
}
LOGGER.info(getClass().getSimpleName() + ": Generated " + (_items.size() - previousCount) + " crystallizable items from templates.");
final int generated = _items.size() - previousCount;
if (generated > 0)
{
LOGGER.info(getClass().getSimpleName() + ": Generated " + generated + " crystallizable items from templates.");
}
}
public List<ItemChanceHolder> getCrystallizationTemplate(CrystalType crystalType, CrystallizationType crystallizationType)
@@ -235,6 +279,49 @@ public class ItemCrystallizationData implements IXmlReader
return result;
}
private RewardItemsOnFailure getFormedHolder(Node node)
{
final RewardItemsOnFailure holder = new RewardItemsOnFailure();
for (Node z = node.getFirstChild(); z != null; z = z.getNextSibling())
{
if ("item".equals(z.getNodeName()))
{
final StatSet failItems = new StatSet(parseAttributes(z));
final int itemId = failItems.getInt("id");
final int enchantLevel = failItems.getInt("enchant");
final double chance = failItems.getDouble("chance");
for (CrystalType grade : CrystalType.values())
{
final long count = failItems.getLong("amount" + grade.name(), Integer.MIN_VALUE);
if (count == Integer.MIN_VALUE)
{
continue;
}
holder.addItemToHolder(itemId, grade, enchantLevel, count, chance);
}
}
}
return holder;
}
public ItemChanceHolder getItemOnDestroy(Player player, Item item)
{
if ((player == null) || (item == null))
{
return null;
}
final RewardItemsOnFailure holder = item.isWeapon() ? _weaponDestroyGroup : _armorDestroyGroup;
final CrystalType grade = item.getTemplate().getCrystalTypePlus();
if (holder.checkIfRewardUnavailable(grade, item.getEnchantLevel()))
{
return null;
}
return holder.getRewardItem(grade, item.getEnchantLevel());
}
/**
* Gets the single instance of ItemCrystalizationData.
* @return single instance of ItemCrystalizationData

View File

@@ -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;
}
}

View File

@@ -22,11 +22,13 @@ import org.l2jmobius.Config;
import org.l2jmobius.commons.network.ReadablePacket;
import org.l2jmobius.commons.util.Rnd;
import org.l2jmobius.gameserver.data.xml.EnchantItemData;
import org.l2jmobius.gameserver.data.xml.ItemCrystallizationData;
import org.l2jmobius.gameserver.enums.ItemSkillType;
import org.l2jmobius.gameserver.enums.UserInfoType;
import org.l2jmobius.gameserver.model.World;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.actor.request.EnchantItemRequest;
import org.l2jmobius.gameserver.model.holders.ItemChanceHolder;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.model.item.enchant.EnchantResultType;
import org.l2jmobius.gameserver.model.item.enchant.EnchantScroll;
@@ -353,6 +355,8 @@ public class RequestEnchantItem implements ClientPacket
}
else
{
final ItemChanceHolder destroyReward = ItemCrystallizationData.getInstance().getItemOnDestroy(player, item);
// enchant failed, destroy item
if (player.getInventory().destroyItem("Enchant", item, player, null) == null)
{
@@ -420,6 +424,12 @@ public class RequestEnchantItem implements ClientPacket
player.sendPacket(new EnchantResult(EnchantResult.FAIL, crystalId, count));
}
if ((destroyReward != null) && (Rnd.get(100) < destroyReward.getChance()))
{
player.addItem("Enchant", destroyReward.getId(), destroyReward.getCount(), null, true);
player.sendPacket(new EnchantResult(EnchantResult.FAIL, destroyReward.getId(), (int) destroyReward.getCount()));
}
if (Config.LOG_ITEM_ENCHANTS)
{
final StringBuilder sb = new StringBuilder();