Drop calculation related cleanup.

This commit is contained in:
MobiusDevelopment
2021-10-25 21:06:38 +00:00
parent bd51582cac
commit e71a589d18
107 changed files with 1997 additions and 1790 deletions

View File

@@ -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<DropHolder> dropListDeath = npc.getTemplate().getDropList(DropType.DROP);
final List<DropHolder> dropListSpoil = npc.getTemplate().getDropList(DropType.SPOIL);
final List<DropHolder> dropListDeath = npc.getTemplate().getDropList();
final List<DropHolder> dropListSpoil = npc.getTemplate().getSpoilList();
if ((dropListDeath != null) || (dropListSpoil != null))
{
sb.append("<table width=275 cellpadding=0 cellspacing=0><tr>");
@@ -346,12 +348,15 @@ public class NpcViewMod implements IBypassHandler
private void sendNpcDropList(PlayerInstance player, Npc npc, DropType dropType, int pageValue)
{
final List<DropHolder> dropList = npc.getTemplate().getDropList(dropType);
if (dropList == null)
final List<DropHolder> templateList = dropType == DropType.SPOIL ? npc.getTemplate().getSpoilList() : npc.getTemplate().getDropList();
if (templateList == null)
{
return;
}
final List<DropHolder> 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())
{

View File

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

View File

@@ -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<Integer> 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());
}
}
}

View File

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

View File

@@ -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<DropHolder> getDropList(DropType dropType)
public List<DropHolder> getDropList()
{
switch (dropType)
{
case DROP:
case LUCKY: // never happens
{
return _dropListDeath;
}
case SPOIL:
{
return _dropListSpoil;
}
}
return null;
return _dropListDeath;
}
public Collection<ItemHolder> calculateDrops(DropType dropType, Creature victim, Creature killer)
public List<DropHolder> getSpoilList()
{
final List<DropHolder> templateList = getDropList(dropType);
if (templateList == null)
return _dropListSpoil;
}
public List<ItemHolder> calculateDrops(DropType dropType, Creature victim, Creature killer)
{
final List<DropHolder> dropList = dropType == DropType.SPOIL ? _dropListSpoil : _dropListDeath;
if (dropList == null)
{
return null;
}
final List<DropHolder> 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<ItemHolder> calculatedDrops = null;
for (DropHolder dropItem : dropList)
List<ItemHolder> calculatedDrops = null;
List<ItemHolder> 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<ItemHolder> items, Creature victim, Creature killer)
private void processVipDrops(List<ItemHolder> items, Creature victim, Creature killer)
{
final List<DropHolder> 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);