Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package events;
- import java.io.File;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.stream.IntStream;
- import javax.xml.parsers.DocumentBuilder;
- import javax.xml.parsers.DocumentBuilderFactory;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.w3c.dom.Document;
- import org.w3c.dom.Element;
- import org.w3c.dom.Node;
- import org.w3c.dom.NodeList;
- import l2.commons.util.Rnd;
- import l2.gameserver.ThreadPoolManager;
- import l2.gameserver.listener.actor.OnDeathListener;
- import l2.gameserver.listener.actor.OnMagicUseListener;
- import l2.gameserver.model.Creature;
- import l2.gameserver.model.Party;
- import l2.gameserver.model.Player;
- import l2.gameserver.model.Skill;
- import l2.gameserver.model.Skill.SkillType;
- import l2.gameserver.model.actor.listener.CharListenerList;
- import l2.gameserver.model.instances.MonsterInstance;
- import l2.gameserver.network.l2.components.SystemMsg;
- import l2.gameserver.network.l2.s2c.SystemMessage;
- import l2.gameserver.scripts.Functions;
- import l2.gameserver.scripts.ScriptFile;
- import l2.gameserver.utils.ItemFunctions;
- /**
- * @author Trance
- */
- public class XmlDropEvent extends Functions implements OnDeathListener, OnMagicUseListener, ScriptFile
- {
- private static final Logger _log = LoggerFactory.getLogger(XmlDropEvent.class);
- private static final String CONFIG_PATH = "./config/drops.xml";
- private static final XmlDropEvent _instance = new XmlDropEvent();
- private static boolean isActive = false;
- private static boolean eventEnabled = false;
- private static boolean debugEnabled = false;
- private static final int REWARD_RANGE = 2000;
- private static final int NUM_SHARDS = 4;
- private static final Map<Integer, List<DropItem>>[] npcDropsShards = createShardedMaps();
- private static final Map<Integer, List<DropItem>>[] npcSweepDropsShards = createShardedMaps();
- private static final Map<Integer, List<RewardItem>>[] storedSweepItemsShards = createShardedSweepMaps();
- private static final Map<Integer, Integer> roundRobinIndices = new ConcurrentHashMap<>();
- public static final int ITEM_LOOTER = 0;
- public static final int ITEM_ORDER = 1;
- public static final int ITEM_ORDER_SPOIL = 2;
- public static final int ITEM_RANDOM = 3;
- public static final int ITEM_RANDOM_SPOIL = 4;
- private static Map<Integer, List<DropItem>>[] createShardedMaps()
- {
- Map<Integer, List<DropItem>>[] shards = new Map[NUM_SHARDS];
- for (int i = 0; i < NUM_SHARDS; i++)
- shards[i] = new ConcurrentHashMap<>();
- return shards;
- }
- private static Map<Integer, List<RewardItem>>[] createShardedSweepMaps()
- {
- Map<Integer, List<RewardItem>>[] shards = new Map[NUM_SHARDS];
- for (int i = 0; i < NUM_SHARDS; i++)
- shards[i] = new ConcurrentHashMap<>();
- return shards;
- }
- public static void loadDrops()
- {
- try
- {
- File file = new File(CONFIG_PATH);
- DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
- DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
- Document doc = dBuilder.parse(file);
- doc.getDocumentElement().normalize();
- NodeList configList = doc.getElementsByTagName("config");
- if (configList.getLength() > 0)
- {
- Element configElement = (Element) configList.item(0);
- eventEnabled = Boolean.parseBoolean(configElement.getAttribute("enabled"));
- debugEnabled = Boolean.parseBoolean(configElement.getAttribute("debug"));
- if (!eventEnabled)
- {
- _log.info("XML Drop Event is disabled in the configuration.");
- return;
- }
- }
- else
- {
- _log.warn("No config found, defaulting to enabled and debug disabled.");
- eventEnabled = true;
- debugEnabled = false;
- }
- for (Map<Integer, List<DropItem>> shard : npcDropsShards)
- shard.clear();
- for (Map<Integer, List<DropItem>> shard : npcSweepDropsShards)
- shard.clear();
- for (Map<Integer, List<RewardItem>> shard : storedSweepItemsShards)
- shard.clear();
- NodeList npcList = doc.getElementsByTagName("npc");
- IntStream.range(0, npcList.getLength()).mapToObj(npcList::item).filter(node -> node.getNodeType() == Node.ELEMENT_NODE).forEach(node ->
- {
- Element npcElement = (Element) node;
- String[] npcIdsArray = npcElement.getAttribute("ids").split(",");
- boolean isSweep = Boolean.parseBoolean(npcElement.getAttribute("sweep"));
- NodeList rewardList = npcElement.getElementsByTagName("reward");
- List<DropItem> dropItems = new ArrayList<>(rewardList.getLength());
- for (int j = 0; j < rewardList.getLength(); j++)
- {
- Element rewardElement = (Element) rewardList.item(j);
- int itemId = Integer.parseInt(rewardElement.getAttribute("item_id"));
- @SuppressWarnings("unused")
- String itemName = rewardElement.getAttribute("item_name");
- int min = Integer.parseInt(rewardElement.getAttribute("min"));
- int max = Integer.parseInt(rewardElement.getAttribute("max"));
- double chance = Double.parseDouble(rewardElement.getAttribute("chance"));
- if (chance > 0)
- dropItems.add(new DropItem(itemId, min, max, chance));
- }
- List<DropItem> immutableDropItems = Collections.unmodifiableList(dropItems);
- for (String npcIdString : npcIdsArray)
- {
- int npcId = Integer.parseInt(npcIdString.trim());
- int shardIndex = Math.abs(npcId) % npcDropsShards.length;
- if (isSweep)
- npcSweepDropsShards[shardIndex].put(npcId, immutableDropItems);
- else
- npcDropsShards[shardIndex].put(npcId, immutableDropItems);
- }
- });
- _log.info("XML drops loaded successfully.");
- }
- catch (Exception e)
- {
- _log.error("Failed to load drops from XML.", e);
- }
- }
- @Override
- public void onDeath(Creature npc, Creature killer)
- {
- if (!eventEnabled || !(npc instanceof MonsterInstance) || killer == null)
- return;
- MonsterInstance monster = (MonsterInstance) npc;
- Player topHater = getMostHatedInRange(monster, REWARD_RANGE);
- Player recipient = (topHater != null) ? topHater : (killer instanceof Player ? (Player) killer : null);
- if (recipient == null)
- return;
- int shardIndex = Math.abs(monster.getNpcId()) % npcDropsShards.length;
- List<DropItem> normalDrops = Optional.ofNullable(npcDropsShards[shardIndex].get(monster.getNpcId())).orElse(Collections.emptyList());
- List<DropItem> sweepDrops = Optional.ofNullable(npcSweepDropsShards[shardIndex].get(monster.getNpcId())).orElse(Collections.emptyList());
- roundRobinIndices.putIfAbsent(monster.getObjectId(), 0);
- ThreadPoolManager.getInstance().execute(() ->
- {
- if (!normalDrops.isEmpty())
- applyDrops(monster, recipient, normalDrops);
- if (!sweepDrops.isEmpty())
- storeSweepDrops(monster, recipient, sweepDrops);
- });
- }
- private Player getMostHatedInRange(MonsterInstance monster, int range)
- {
- Creature mostHated = monster.getAggroList().getMostHated();
- if (mostHated instanceof Player && mostHated.isInRange(monster, range))
- return (Player) mostHated;
- return null;
- }
- private void applyDrops(MonsterInstance monster, Player player, List<DropItem> drops)
- {
- if (monster == null || player == null || drops == null)
- return;
- Party playerParty = player.getParty();
- int lootDistribution = playerParty != null ? playerParty.getLootDistribution() : ITEM_LOOTER;
- List<Player> eligiblePlayers = new ArrayList<>(playerParty != null ? playerParty.getPartyMembers() : Collections.singletonList(player));
- eligiblePlayers.removeIf(p -> !p.isInRange(monster, REWARD_RANGE));
- int eligibleSize = eligiblePlayers.size();
- if (eligibleSize == 0)
- return;
- for (DropItem drop : drops)
- {
- if (Rnd.chance(drop.getChance()))
- {
- int itemCount = Rnd.get(drop.getMin(), drop.getMax());
- Player recipient = player;
- synchronized (roundRobinIndices)
- {
- switch (lootDistribution)
- {
- case ITEM_LOOTER:
- recipient = player;
- break;
- case ITEM_ORDER:
- case ITEM_ORDER_SPOIL:
- int roundRobinIndex = roundRobinIndices.get(monster.getObjectId());
- recipient = eligiblePlayers.get(roundRobinIndex % eligibleSize);
- roundRobinIndices.put(monster.getObjectId(), roundRobinIndex + 1);
- break;
- case ITEM_RANDOM:
- case ITEM_RANDOM_SPOIL:
- recipient = eligiblePlayers.get(Rnd.get(eligibleSize));
- break;
- }
- }
- ItemFunctions.addItem(recipient, drop.getItemId(), itemCount, false);
- recipient.sendPacket(new SystemMessage(SystemMsg.YOU_HAVE_OBTAINED_S2_S1).addItemName(drop.getItemId()).addNumber(itemCount));
- for (Player partyMember : eligiblePlayers)
- {
- if (partyMember != recipient)
- partyMember.sendPacket(new SystemMessage(SystemMsg.C1_HAS_OBTAINED_S3_S2).addName(recipient).addItemName(drop.getItemId()).addNumber(itemCount));
- }
- if (debugEnabled && _log.isInfoEnabled())
- _log.info("Loot Type: {} - Dropped item ID {} (amount: {}) for player {} from NPC {}", lootDistribution, drop.getItemId(), itemCount, recipient.getName(), monster.getNpcId());
- }
- }
- }
- private void storeSweepDrops(MonsterInstance monster, Player player, List<DropItem> sweepDrops)
- {
- if (monster == null || player == null || sweepDrops == null)
- return;
- List<RewardItem> rewardItems = new ArrayList<>(sweepDrops.size());
- for (DropItem drop : sweepDrops)
- {
- if (Rnd.chance(drop.getChance()))
- {
- int itemCount = Rnd.get(drop.getMin(), drop.getMax());
- rewardItems.add(new RewardItem(drop.getItemId(), itemCount));
- }
- }
- if (!rewardItems.isEmpty())
- {
- int shardIndex = monster.getObjectId() % storedSweepItemsShards.length;
- storedSweepItemsShards[shardIndex].put(monster.getObjectId(), Collections.unmodifiableList(rewardItems));
- }
- }
- private List<RewardItem> getStoredSweepDrops(int monsterObjectId)
- {
- int shardIndex = monsterObjectId % storedSweepItemsShards.length;
- return Optional.ofNullable(storedSweepItemsShards[shardIndex].get(monsterObjectId)).orElse(Collections.emptyList());
- }
- @Override
- public void onMagicUse(Creature actor, Skill skill, Creature target, boolean alt)
- {
- if (!eventEnabled || !(actor instanceof Player) || !(target instanceof MonsterInstance))
- return;
- Player player = (Player) actor;
- MonsterInstance monster = (MonsterInstance) target;
- if (skill.getSkillType() == SkillType.SWEEP && monster.isSpoiled(player))
- {
- List<RewardItem> sweepItems = getStoredSweepDrops(monster.getObjectId());
- if (!sweepItems.isEmpty())
- {
- applySweepDrops(player, sweepItems);
- int shardIndex = monster.getObjectId() % storedSweepItemsShards.length;
- storedSweepItemsShards[shardIndex].remove(monster.getObjectId());
- monster.clearSweep();
- }
- }
- }
- private void applySweepDrops(Player player, List<RewardItem> sweepItems)
- {
- if (player == null || sweepItems == null)
- return;
- Party playerParty = player.getParty();
- int lootDistribution = playerParty != null ? playerParty.getLootDistribution() : ITEM_LOOTER;
- List<Player> eligiblePlayers = new ArrayList<>(playerParty != null ? playerParty.getPartyMembers() : Collections.singletonList(player));
- eligiblePlayers.removeIf(p -> !p.isInRange(player, REWARD_RANGE));
- int eligibleSize = eligiblePlayers.size();
- if (eligibleSize == 0)
- return;
- for (RewardItem item : sweepItems)
- {
- Player recipient = player;
- synchronized (roundRobinIndices)
- {
- switch (lootDistribution)
- {
- case ITEM_ORDER_SPOIL:
- int roundRobinIndex = roundRobinIndices.getOrDefault(player.getObjectId(), 0);
- recipient = eligiblePlayers.get(roundRobinIndex % eligibleSize);
- roundRobinIndices.put(player.getObjectId(), roundRobinIndex + 1);
- break;
- case ITEM_RANDOM_SPOIL:
- recipient = eligiblePlayers.get(Rnd.get(eligibleSize));
- break;
- default:
- recipient = player;
- break;
- }
- }
- ItemFunctions.addItem(recipient, item.getItemId(), item.getCount(), false);
- recipient.sendPacket(new SystemMessage(SystemMsg.YOU_HAVE_OBTAINED_S2_S1).addItemName(item.getItemId()).addNumber(item.getCount()));
- for (Player partyMember : eligiblePlayers)
- {
- if (partyMember != recipient)
- partyMember.sendPacket(new SystemMessage(SystemMsg.C1_HAS_OBTAINED_S3_S2_BY_USING_SWEEPER).addName(recipient).addItemName(item.getItemId()).addNumber(item.getCount()));
- }
- if (debugEnabled && _log.isInfoEnabled())
- _log.info("Loot Type: Sweep - Collected item ID {} (amount: {}) for player {}", item.getItemId(), item.getCount(), recipient.getName());
- }
- }
- public void startEvent()
- {
- if (!isActive)
- {
- loadDrops();
- if (eventEnabled)
- {
- CharListenerList.addGlobal(_instance);
- isActive = true;
- _log.info("XML Drop Event started.");
- }
- }
- else
- {
- _log.info("XML Drop Event already active.");
- }
- }
- public void stopEvent()
- {
- if (isActive)
- {
- CharListenerList.removeGlobal(_instance);
- isActive = false;
- _log.info("XML Drop Event stopped.");
- }
- }
- @Override
- public void onLoad()
- {
- startEvent();
- }
- @Override
- public void onReload()
- {
- stopEvent();
- onLoad();
- }
- @Override
- public void onShutdown()
- {
- stopEvent();
- }
- public static boolean isEventEnabled()
- {
- return eventEnabled;
- }
- private static class DropItem
- {
- private final int itemId;
- private final int min;
- private final int max;
- private final double chance;
- public DropItem(int itemId, int min, int max, double chance)
- {
- this.itemId = itemId;
- this.min = min;
- this.max = max;
- this.chance = chance;
- }
- public int getItemId()
- {
- return itemId;
- }
- public int getMin()
- {
- return min;
- }
- public int getMax()
- {
- return max;
- }
- public double getChance()
- {
- return chance;
- }
- }
- private static class RewardItem
- {
- private final int itemId;
- private final int count;
- public RewardItem(int itemId, int count)
- {
- this.itemId = itemId;
- this.count = count;
- }
- public int getItemId()
- {
- return itemId;
- }
- public int getCount()
- {
- return count;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement