diff --git a/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append(""); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_01.0_Ertheia/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 4079fc9115..54f833e775 100644 --- a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1104,6 +1104,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1141,6 +1142,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_01.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_02.5_Underground/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/Attackable.java index e8120b514c..11d30da35d 100644 --- a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_02.5_Underground/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_03.0_Helios/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/Attackable.java index e8120b514c..11d30da35d 100644 --- a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_03.0_Helios/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_04.0_GrandCrusade/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Attackable.java index e8120b514c..11d30da35d 100644 --- a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_04.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_05.0_Salvation/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 83dfb25fcb..d496fa5b78 100644 --- a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1086,6 +1086,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1123,6 +1124,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_05.0_Salvation/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_05.5_EtinasFate/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 83dfb25fcb..d496fa5b78 100644 --- a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1086,6 +1086,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1123,6 +1124,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_05.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_06.0_Fafurion/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 83dfb25fcb..d496fa5b78 100644 --- a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1086,6 +1086,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1123,6 +1124,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_06.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_07.0_PreludeOfWar/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/NpcData.java index fa3ae59081..129d640563 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 12aea5249f..826688bfbd 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1097,6 +1097,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1134,6 +1135,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 2a0016df71..3deef75027 100644 --- a/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_07.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 46fbc3e377..bb32246396 100644 --- a/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_08.2_Homunculus/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/NpcData.java index a9308e249c..e4026cf322 100644 --- a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 12aea5249f..826688bfbd 100644 --- a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1097,6 +1097,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1134,6 +1135,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 8b2fa1c6e0..0dd4b41ae1 100644 --- a/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_08.2_Homunculus/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 46fbc3e377..bb32246396 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/NpcData.java index a9308e249c..e4026cf322 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -614,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // Lucky drops are added to normal drops and calculated later + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 12aea5249f..826688bfbd 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1097,6 +1097,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1134,6 +1135,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 8b2fa1c6e0..0dd4b41ae1 100644 --- a/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_09.2_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 5c8debfda2..55d1010c1f 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -204,8 +206,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -226,12 +228,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index f2275aadd9..1ba45e9737 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -102,16 +102,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 77d7c2ae01..6f4e0d65b6 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -632,6 +632,7 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) @@ -722,11 +723,11 @@ public class NpcData implements IXmlReader public List getTemplates(Predicate filter) { final List result = new ArrayList<>(); - for (NpcTemplate template : _npcs.values()) + for (NpcTemplate npcTemplate : _npcs.values()) { - if (filter.test(template)) + if (filter.test(npcTemplate)) { - result.add(template); + result.add(npcTemplate); } } return result; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Attackable.java index c13009a375..663bfce032 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1058,6 +1058,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1096,6 +1097,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index fee95e6d68..88fcff8d57 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -607,81 +606,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java index 297179b984..c270373283 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java @@ -196,7 +196,7 @@ public class PlayerTemplate extends CreatureTemplate /** * @param slotId id of inventory slot to return value - * @return defence value of charactert for EMPTY given slot + * @return defense value of character for EMPTY given slot */ public int getBaseDefBySlot(int slotId) { diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index cc5a910a8f..9a88fcdd12 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -204,8 +206,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -226,12 +228,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index f2275aadd9..1ba45e9737 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -102,16 +102,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 77d7c2ae01..6f4e0d65b6 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -632,6 +632,7 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) @@ -722,11 +723,11 @@ public class NpcData implements IXmlReader public List getTemplates(Predicate filter) { final List result = new ArrayList<>(); - for (NpcTemplate template : _npcs.values()) + for (NpcTemplate npcTemplate : _npcs.values()) { - if (filter.test(template)) + if (filter.test(npcTemplate)) { - result.add(template); + result.add(npcTemplate); } } return result; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Attackable.java index c13009a375..663bfce032 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1058,6 +1058,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1096,6 +1097,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index fee95e6d68..88fcff8d57 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -607,81 +606,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java index 297179b984..c270373283 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/templates/PlayerTemplate.java @@ -196,7 +196,7 @@ public class PlayerTemplate extends CreatureTemplate /** * @param slotId id of inventory slot to return value - * @return defence value of charactert for EMPTY given slot + * @return defense value of character for EMPTY given slot */ public int getBaseDefBySlot(int slotId) { diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/xml/NpcData.java index aeea147915..129d640563 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -421,16 +421,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -440,16 +441,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -461,16 +461,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -624,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -643,12 +634,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 6763b71d25..4f820640f3 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index fa98330dcd..15b4ad5bf4 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -669,75 +668,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -769,7 +782,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -808,14 +821,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/xml/NpcData.java index aeea147915..129d640563 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -421,16 +421,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -440,16 +441,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -461,16 +461,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -624,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -643,12 +634,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 6763b71d25..4f820640f3 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index fa98330dcd..15b4ad5bf4 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -669,75 +668,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -769,7 +782,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -808,14 +821,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/xml/NpcData.java index aeea147915..129d640563 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -204,10 +204,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -421,16 +421,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -440,16 +441,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -461,16 +461,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -624,12 +614,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -643,12 +634,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 7229823e3b..b2f8bec908 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1100,6 +1100,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1137,6 +1138,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index fa98330dcd..15b4ad5bf4 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -669,75 +668,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -769,7 +782,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -808,14 +821,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 46a27810f6..4cf0e0eacc 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -207,10 +207,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -424,16 +424,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -443,16 +444,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -464,16 +464,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -627,12 +617,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -646,12 +637,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 42117c0eb3..cc4c5d3767 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1121,6 +1121,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1158,6 +1159,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index e2c95027db..3938eeabc6 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -684,75 +683,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -784,7 +797,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -823,14 +836,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 46a27810f6..4cf0e0eacc 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -207,10 +207,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -424,16 +424,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -443,16 +444,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -464,16 +464,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -627,12 +617,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -646,12 +637,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 42117c0eb3..cc4c5d3767 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1121,6 +1121,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1158,6 +1159,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index e2c95027db..3938eeabc6 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -684,75 +683,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -784,7 +797,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -823,14 +836,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 10d4201c2e..57dc421d9d 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 46a27810f6..4cf0e0eacc 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -207,10 +207,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -424,16 +424,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -443,16 +444,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -464,16 +464,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -627,12 +617,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -646,12 +637,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 42117c0eb3..cc4c5d3767 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1121,6 +1121,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1158,6 +1159,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index e2c95027db..3938eeabc6 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -684,75 +683,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop = calculateLevelGapChanceToDrop(dropItem, levelDifference); - - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop @@ -784,7 +797,7 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable return calculatedDrops; } - private void processVipDrops(Collection items, Creature victim, Creature killer) + private void processVipDrops(List items, Creature victim, Creature killer) { final List dropList = new ArrayList<>(); if (killer.getActingPlayer() != null) @@ -823,14 +836,12 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable { return null; } - return calculateDrop(dropItem, victim, killer); } private double calculateLevelGapChanceToDrop(DropHolder dropItem, int levelDifference) { final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) { levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index 0f5a9295c9..849a22c95e 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 948631baee..8ed96e75e1 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 15e979618b..a31c727d64 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -206,10 +206,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -423,16 +423,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -442,17 +443,16 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - final Item item = ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")); + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + final Item item = ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")); if (item == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -471,16 +471,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -634,12 +624,13 @@ public class NpcData implements IXmlReader if (dropLists != null) { + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -653,12 +644,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Attackable.java index b7ae2d6052..01858bcbd7 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1104,6 +1104,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1141,6 +1142,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index ffbb72d924..af80fd1293 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -668,82 +667,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index ff4ff41bbe..8e2bc62bd2 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 83ed7318ad..4af69793bf 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 70c2d2b8c8..81eb65aea7 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -98,7 +98,7 @@ public class NpcData implements IXmlReader final StatSet set = new StatSet(new HashMap<>()); final int npcId = parseInteger(attrs, "id"); final int level = parseInteger(attrs, "level", 85); - final String type; + final String type = parseString(attrs, "type"); Map parameters = null; Map skills = null; Set clans = null; @@ -107,7 +107,6 @@ public class NpcData implements IXmlReader set.set("id", npcId); set.set("displayId", parseInteger(attrs, "displayId")); set.set("level", level); - type = parseString(attrs, "type"); set.set("type", type); set.set("name", parseString(attrs, "name")); set.set("usingServerSideName", parseBoolean(attrs, "usingServerSideName")); @@ -211,10 +210,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -428,16 +427,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -447,16 +447,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -468,16 +467,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -643,6 +632,7 @@ public class NpcData implements IXmlReader dropLists.add(new DropHolder(DropType.DROP, Inventory.LCOIN_ID, Config.LCOIN_MIN_QUANTITY, Config.LCOIN_MAX_QUANTITY, Config.LCOIN_DROP_CHANCE)); } + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { // Drop materials for random craft configuration. @@ -654,7 +644,7 @@ public class NpcData implements IXmlReader switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -668,12 +658,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Attackable.java index 2a6831ec2f..9e2471d550 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1126,6 +1126,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1163,6 +1164,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 7d24dac643..0b095f02e2 100644 --- a/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Essence_4.2_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -683,82 +682,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop diff --git a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java index ff4ff41bbe..8e2bc62bd2 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/bypasshandlers/NpcViewMod.java @@ -17,6 +17,8 @@ package handlers.bypasshandlers; import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -324,8 +326,8 @@ public class NpcViewMod implements IBypassHandler private static String getDropListButtons(Npc npc) { final StringBuilder sb = new StringBuilder(); - final List dropListDeath = npc.getTemplate().getDropList(DropType.DROP); - final List dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL); + final List dropListDeath = npc.getTemplate().getDropList(); + final List dropListSpoil = npc.getTemplate().getSpoilList(); if ((dropListDeath != null) || (dropListSpoil != null)) { sb.append("
"); @@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue) { - final List dropList = npc.getTemplate().getDropList(dropType); - if (dropList == null) + final List templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList(); + if (templateList == null) { return; } + final List dropList = new ArrayList<>(templateList); + Collections.sort(dropList, (d1, d2) -> Integer.valueOf(d1.getItemId()).compareTo(Integer.valueOf(d2.getItemId()))); + int pages = dropList.size() / DROP_LIST_ITEMS_PER_PAGE; if ((DROP_LIST_ITEMS_PER_PAGE * pages) < dropList.size()) { diff --git a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java index 83ed7318ad..4af69793bf 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/dist/game/data/scripts/handlers/communityboard/DropSearchBoard.java @@ -104,16 +104,16 @@ public class DropSearchBoard implements IParseBoardHandler private void buildDropIndex() { - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.DROP) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getDropList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.DROP)) + for (DropHolder dropHolder : npcTemplate.getDropList()) { addToDropList(npcTemplate, dropHolder); } }); - NpcData.getInstance().getTemplates(npc -> npc.getDropList(DropType.SPOIL) != null).forEach(npcTemplate -> + NpcData.getInstance().getTemplates(npc -> npc.getSpoilList() != null).forEach(npcTemplate -> { - for (DropHolder dropHolder : npcTemplate.getDropList(DropType.SPOIL)) + for (DropHolder dropHolder : npcTemplate.getSpoilList()) { addToDropList(npcTemplate, dropHolder); } diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/NpcData.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/NpcData.java index 70c2d2b8c8..81eb65aea7 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/NpcData.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/data/xml/NpcData.java @@ -98,7 +98,7 @@ public class NpcData implements IXmlReader final StatSet set = new StatSet(new HashMap<>()); final int npcId = parseInteger(attrs, "id"); final int level = parseInteger(attrs, "level", 85); - final String type; + final String type = parseString(attrs, "type"); Map parameters = null; Map skills = null; Set clans = null; @@ -107,7 +107,6 @@ public class NpcData implements IXmlReader set.set("id", npcId); set.set("displayId", parseInteger(attrs, "displayId")); set.set("level", level); - type = parseString(attrs, "type"); set.set("type", type); set.set("name", parseString(attrs, "name")); set.set("usingServerSideName", parseBoolean(attrs, "usingServerSideName")); @@ -211,10 +210,10 @@ public class NpcData implements IXmlReader } case "attribute": { - for (Node attribute_node = statsNode.getFirstChild(); attribute_node != null; attribute_node = attribute_node.getNextSibling()) + for (Node attributeNode = statsNode.getFirstChild(); attributeNode != null; attributeNode = attributeNode.getNextSibling()) { - attrs = attribute_node.getAttributes(); - switch (attribute_node.getNodeName().toLowerCase()) + attrs = attributeNode.getAttributes(); + switch (attributeNode.getNodeName().toLowerCase()) { case "attack": { @@ -428,16 +427,17 @@ public class NpcData implements IXmlReader } case "droplists": { - for (Node drop_lists_node = npcNode.getFirstChild(); drop_lists_node != null; drop_lists_node = drop_lists_node.getNextSibling()) + for (Node dropListsNode = npcNode.getFirstChild(); dropListsNode != null; dropListsNode = dropListsNode.getNextSibling()) { DropType dropType = null; try { - dropType = Enum.valueOf(DropType.class, drop_lists_node.getNodeName().toUpperCase()); + dropType = Enum.valueOf(DropType.class, dropListsNode.getNodeName().toUpperCase()); } catch (Exception e) { + // Handled bellow. } if (dropType != null) @@ -447,16 +447,15 @@ public class NpcData implements IXmlReader dropLists = new ArrayList<>(); } - for (Node drop_node = drop_lists_node.getFirstChild(); drop_node != null; drop_node = drop_node.getNextSibling()) + for (Node dropNode = dropListsNode.getFirstChild(); dropNode != null; dropNode = dropNode.getNextSibling()) { - final NamedNodeMap drop_attrs = drop_node.getAttributes(); - if ("item".equals(drop_node.getNodeName().toLowerCase())) + final NamedNodeMap dropAttrs = dropNode.getAttributes(); + if ("item".equalsIgnoreCase(dropNode.getNodeName())) { - final double chance = parseDouble(drop_attrs, "chance"); - final DropHolder dropItem = new DropHolder(dropType, parseInteger(drop_attrs, "id"), parseLong(drop_attrs, "min"), parseLong(drop_attrs, "max"), dropType == DropType.LUCKY ? chance / 100 : chance); - if (ItemTable.getInstance().getTemplate(parseInteger(drop_attrs, "id")) == null) + final DropHolder dropItem = new DropHolder(dropType, parseInteger(dropAttrs, "id"), parseLong(dropAttrs, "min"), parseLong(dropAttrs, "max"), parseDouble(dropAttrs, "chance")); + if (ItemTable.getInstance().getTemplate(parseInteger(dropAttrs, "id")) == null) { - LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(drop_attrs, "id") + "."); + LOGGER.warning("DropListItem: Could not find item with id " + parseInteger(dropAttrs, "id") + "."); } else { @@ -468,16 +467,6 @@ public class NpcData implements IXmlReader } break; } - case "extenddrop": - { - final List extendDrop = new ArrayList<>(); - forEach(npcNode, "id", idNode -> - { - extendDrop.add(Integer.parseInt(idNode.getTextContent())); - }); - set.set("extendDrop", extendDrop); - break; - } case "collision": { for (Node collisionNode = npcNode.getFirstChild(); collisionNode != null; collisionNode = collisionNode.getNextSibling()) @@ -643,6 +632,7 @@ public class NpcData implements IXmlReader dropLists.add(new DropHolder(DropType.DROP, Inventory.LCOIN_ID, Config.LCOIN_MIN_QUANTITY, Config.LCOIN_MAX_QUANTITY, Config.LCOIN_DROP_CHANCE)); } + Collections.shuffle(dropLists); for (DropHolder dropHolder : dropLists) { // Drop materials for random craft configuration. @@ -654,7 +644,7 @@ public class NpcData implements IXmlReader switch (dropHolder.getDropType()) { case DROP: - case LUCKY: // TODO: Luck is added to death drops. + case LUCKY: // Lucky drops are added to normal drops and calculated later. { template.addDrop(dropHolder); break; @@ -668,12 +658,9 @@ public class NpcData implements IXmlReader } } - if (!template.getParameters().getMinionList("Privates").isEmpty()) + if (!template.getParameters().getMinionList("Privates").isEmpty() && (template.getParameters().getSet().get("SummonPrivateRate") == null)) { - if (template.getParameters().getSet().get("SummonPrivateRate") == null) - { - _masterMonsterIDs.add(template.getId()); - } + _masterMonsterIDs.add(template.getId()); } } } diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/Attackable.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/Attackable.java index c25d223ae1..a818c31110 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/Attackable.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/Attackable.java @@ -1131,6 +1131,7 @@ public class Attackable extends Npc } } } + deathItems.clear(); } } return; @@ -1168,6 +1169,7 @@ public class Attackable extends Npc broadcastPacket(sm); } } + deathItems.clear(); } } diff --git a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java index 7d24dac643..0b095f02e2 100644 --- a/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java +++ b/L2J_Mobius_Essence_5.2_FrostLord/java/org/l2jmobius/gameserver/model/actor/templates/NpcTemplate.java @@ -17,7 +17,6 @@ package org.l2jmobius.gameserver.model.actor.templates; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -683,82 +682,89 @@ public class NpcTemplate extends CreatureTemplate implements IIdentifiable _dropListSpoil.add(dropHolder); } - public List getDropList(DropType dropType) + public List getDropList() { - switch (dropType) - { - case DROP: - case LUCKY: // never happens - { - return _dropListDeath; - } - case SPOIL: - { - return _dropListSpoil; - } - } - return null; + return _dropListDeath; } - public Collection calculateDrops(DropType dropType, Creature victim, Creature killer) + public List getSpoilList() { - final List templateList = getDropList(dropType); - if (templateList == null) + return _dropListSpoil; + } + + public List calculateDrops(DropType dropType, Creature victim, Creature killer) + { + final List dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath; + if (dropList == null) { return null; } - final List dropList = new ArrayList<>(templateList); - - // randomize drop order - Collections.shuffle(dropList); - + // level difference calculations final int levelDifference = victim.getLevel() - killer.getLevel(); + final double levelGapChanceToDropAdena = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100d); + final double levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100d); + int dropOccurrenceCounter = victim.isRaid() ? Config.DROP_MAX_OCCURRENCES_RAIDBOSS : Config.DROP_MAX_OCCURRENCES_NORMAL; - Collection calculatedDrops = null; - for (DropHolder dropItem : dropList) + List calculatedDrops = null; + List randomDrops = null; + ItemHolder replacedItem = null; + if (dropOccurrenceCounter > 0) { - // check if maximum drop occurrences have been reached - // items that have 100% drop chance without server rate multipliers drop normally - if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100)) + for (DropHolder dropItem : dropList) { - continue; + // check if maximum drop occurrences have been reached + // items that have 100% drop chance without server rate multipliers drop normally + if ((dropOccurrenceCounter == 0) && (dropItem.getChance() < 100) && (randomDrops != null) && (calculatedDrops != null)) + { + // remove a random existing drop (temporarily if not other item replaces it) + dropOccurrenceCounter++; + replacedItem = randomDrops.remove(Rnd.get(randomDrops.size())); + calculatedDrops.remove(replacedItem); + } + + // check level gap that may prevent to drop item + if ((Rnd.nextDouble() * 100) > (dropItem.getItemId() == Inventory.ADENA_ID ? levelGapChanceToDropAdena : levelGapChanceToDrop)) + { + continue; + } + + // calculate chances + final ItemHolder drop = calculateDrop(dropItem, victim, killer); + if (drop == null) + { + continue; + } + + // create lists + if (randomDrops == null) + { + randomDrops = new ArrayList<>(dropOccurrenceCounter); + } + if (calculatedDrops == null) + { + calculatedDrops = new ArrayList<>(dropOccurrenceCounter); + } + + // finally + if (dropItem.getChance() < 100) + { + dropOccurrenceCounter--; + randomDrops.add(drop); + } + calculatedDrops.add(drop); } - - // check level gap that may prevent drop this item - final double levelGapChanceToDrop; - if (dropItem.getItemId() == Inventory.ADENA_ID) - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0); - } - else - { - levelGapChanceToDrop = Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0); - } - if ((Rnd.nextDouble() * 100) > levelGapChanceToDrop) - { - continue; - } - - // calculate chances - final ItemHolder drop = calculateDrop(dropItem, victim, killer); - if (drop == null) - { - continue; - } - - // create list - if (calculatedDrops == null) - { - calculatedDrops = new ArrayList<>(); - } - - // finally - if (dropItem.getChance() < 100) - { - dropOccurrenceCounter--; - } - calculatedDrops.add(drop); + } + // add temporarily removed item when not replaced + if ((dropOccurrenceCounter > 0) && (replacedItem != null) && (calculatedDrops != null)) + { + calculatedDrops.add(replacedItem); + } + // clear random drops + if (randomDrops != null) + { + randomDrops.clear(); + randomDrops = null; } // champion extra drop