/**
* Calculates quantity of items for specific drop acording to current situation <br>
*
* @param drop The L2DropData count is being calculated for
* @param lastAttacker The L2PcInstance that has killed the L2Attackable
* @param deepBlueDrop Factor to divide the drop chance
* @param levelModifier level modifier in %'s (will be subtracted from drop chance)
*/
private int calculateDropCount(L2DropData drop, L2PcInstance lastAttacker, int levelModifier)
{
// Roll the dice.. between 0 and MAX_CHANCE (=1000000)
int roll = Rnd.get(1, L2DropData.MAX_CHANCE);
float rate = (drop.isSweep()? Config.RATE_DROP_SPOIL:Config.RATE_DROP_ITEMS);
// Get default drop chance
float dropChance = drop.getChance() * rate;
int deepBlueDrop = 1;
if (Config.DEEPBLUE_DROP_RULES)
{
if (levelModifier > 0)
{
// We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
// NOTE: This is valid only for adena drops! Others drops will still obey server's rate
deepBlueDrop = 3;
if (drop.getItemId() == 57) deepBlueDrop *= (int)Config.RATE_DROP_ITEMS;
}
}
// Check if we should apply our maths so deep blue mobs will not drop that easy
if (Config.DEEPBLUE_DROP_RULES) dropChance = ((drop.getChance() - ((drop.getChance() * levelModifier)/100)) * rate / deepBlueDrop);
// Round drop chance
dropChance = Math.round(dropChance);
// Set our limits for chance of drop
if (dropChance < 1) dropChance = 1;
if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time
int itemCount = 0;
// Check if the Item must be dropped
while (roll < dropChance)
{
// Get min and max Item quantity that can be dropped in one time
int min = drop.getMinDrop();
int max = drop.getMaxDrop();
// Get the item quantity dropped
if (min < max)
itemCount += Rnd.get(min, max);
else if (min == max)
itemCount += min;
else
itemCount++;
// Calculate the Adena reward with the RATE_DROP_ADENA
if (drop.getItemId() == 57) itemCount *= Config.RATE_DROP_ADENA;
// Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
dropChance -= L2DropData.MAX_CHANCE;
}
if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
return itemCount;
}
/**
* Calculates the level modifier for drop<br>
*
* @param lastAttacker The L2PcInstance that has killed the L2Attackable
*/
private int calculateLevelModifierForDrop(L2PcInstance lastAttacker)
{
if (Config.DEEPBLUE_DROP_RULES)
{
int highestLevel = lastAttacker.getLevel();
// Check to prevent very high level player to nearly kill mob and let low level player do the last hit.
if (getAttackByList() != null && getAttackByList().size() > 0)
{
for (L2Character atkChar: getAttackByList())
if (atkChar != null && atkChar.getLevel() > highestLevel) highestLevel = atkChar.getLevel();
}
// According to official data (Prima), deep blue mobs are 9 or more levels below players
if (highestLevel - 9 >= getLevel()) return ((highestLevel - (getLevel() + 8)) * 9);
}
return 0;
}
private void dropItem(L2PcInstance player, int itemId, int itemCount)
{
L2ItemInstance dropit;
int z;
for (int i = 1; i <= itemCount; i++)
{
dropit = ItemTable.getInstance().createItem(itemId);
// Set the Item quantity dropped if L2ItemInstance is stackable
if (dropit.isStackable())
{
i = itemCount; // Set so loop won't happent again
dropit.setCount(itemCount); // Set item count
}
if (Config.DEBUG) _log.fine("Item id to drop: " + itemId + " amount: " + dropit.getCount());
if(this instanceof L2RaidBossInstance)
{
SystemMessage sm = new SystemMessage(SystemMessage.S1_DIED_DROPPED_S3_S2);
sm.addString(getName());
sm.addNumber(dropit.getCount());
sm.addItemName(itemId);
broadcastPacket(sm);
}
// Check if the autoLoot mode is active
if (Config.AUTO_LOOT)
player.doAutoLoot(dropit); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
else
{
// Get the current Z position of the L2Character that has killed the L2Attackable
z = getZ();
if (player.getZ() > z) z = player.getZ();
// Init the dropped L2ItemInstance and add it in the world as a visible object at the position where mob was last
dropit.dropMe(this, getX(), getY(), z + 200);
// Add drop to auto destroy item task
if (Config.AUTODESTROY_ITEM_AFTER > 0) ItemsAutoDestroy.getInstance().addItem(dropit);
}
}
}
/**
* Manage Sweep drops of L2Attackable.<BR><BR>
*
* <B><U> Actions</U> : </B><BR><BR>
* <li>Get all possible drops of this L2Attackable from L2NpcTemplate</li>
* <li>For each possible drops (base including sweep), calculate which one can is spoiled (random) </li>
* <li>Get each Item spoiled quantity (random) </li>
* <li>Create this or these L2ItemInstance corresponding to each Item Identifier spoiled</li>
* <li>Add each of those L2ItemInstance to the table _sweepItems of this L2Attackable containing all Items that can be spoiled </li><BR><BR>
*
* @param lastAttacker The L2Character that has killed the L2Attackable
*/
public void doSweepDrop(L2Character lastAttacker)
{
L2PcInstance player = null;
if (lastAttacker instanceof L2PcInstance)
player = (L2PcInstance)lastAttacker;
else if (lastAttacker instanceof L2Summon)
player = ((L2Summon)lastAttacker).getOwner();
if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
int levelModifier = calculateLevelModifierForDrop(player);; // level modifier in %'s (will be subtracted from drop chance)
// Create a table containing all possible drops of this L2Attackable from L2NpcTemplate
List<L2DropData> drops = getTemplate().getDropData();
if (Config.DEBUG) _log.finer("this npc has "+drops.size()+" drops defined");
// Create a table that will contain all Items that can be spoiled
List<L2ItemInstance> spoiled = new FastList<L2ItemInstance>();
// Go Throw all possible drops (base including sweep) of this L2Attackable
L2ItemInstance dropit;
int itemCount;
for (L2DropData drop: drops)
{
if (drop == null) continue;
// If it's not a sweep drop, it will not appear on the ground only drop adena if seeded with low-grade seed
if (!drop.isSweep() || (isSeeded() && !isSeededHigh() && drop.getItemId() != 57))
continue;
// Calculates Item drop count with current rates...
itemCount = calculateDropCount(drop, player, levelModifier);
for (int i = 1; i <= itemCount; i++)
{
dropit = ItemTable.getInstance().createItem(drop.getItemId());
// Set the Item quantity dropped if L2ItemInstance is stackable
if (dropit.isStackable())
{
i = itemCount; // Set so loop won't happent again
dropit.setCount(itemCount); // Set item count
}
spoiled.add(dropit);
if (Config.DEBUG) _log.fine("Item id to spoil: " + drop.getItemId() + " amount: " + dropit.getCount());
}
}
// Set the table _sweepItems of this L2Attackable
if (spoiled.size() > 0) _sweepItems = spoiled.toArray(new L2ItemInstance[spoiled.size()]);
}
/**
* Manage Special Events drops created by GM for a defined period.<BR><BR>
*
* <B><U> Concept</U> :</B><BR><BR>
* During a Special Event all L2Attackable can drop extra Items.
* Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
* Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
*
* <B><U> Actions</U> : <I>If an extra drop must be generated</I></B><BR><BR>
* <li>Get an Item Identifier (random) from the DateDrop Item table of this Event </li>
* <li>Get the Item quantity dropped (random) </li>
* <li>Create this or these L2ItemInstance corresponding to this Item Identifier</li>
* <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
* <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
*
* @param lastAttacker The L2Character that has killed the L2Attackable
*/
public void doEventDrop(L2Character lastAttacker)
{
L2PcInstance player = null;
if (lastAttacker instanceof L2PcInstance)
player = (L2PcInstance)lastAttacker;
else if (lastAttacker instanceof L2Summon)
player = ((L2Summon)lastAttacker).getOwner();
if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
if (player.getLevel() - getLevel() > 9)
return;
// Go through DateDrop of EventDroplist allNpcDateDrops within the date range
int itemCount;
int max;
int min;
for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
{
if (Rnd.get(1, L2DropData.MAX_CHANCE) < drop.chance)
{
// Get min and max Item quantity that can be dropped in one time
max = drop.max;
min = drop.min;
// Get the item quantity dropped
itemCount = 0;
if (min < max)
itemCount += Rnd.get(min, max);
else if (min == max)
itemCount += min;
else
itemCount++;
dropItem(player, drop.items[Rnd.get(0, drop.items.length - 1)], itemCount);
}
}
}
/**
* Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<BR><BR>
*
* <B><U> Concept</U> :</B><BR><BR>
* During a Special Event all L2Attackable can drop extra Items.
* Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
* Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
*
* <B><U> Actions</U> : </B><BR><BR>
* <li>Manage drop of Special Events created by GM for a defined period </li>
* <li>Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops</li>
* <li>For each possible drops (base + quests), calculate which one must be dropped (random) </li>
* <li>Get each Item quantity dropped (random) </li>
* <li>Create this or these L2ItemInstance corresponding to each Item Identifier dropped</li>
* <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
* <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
*
* @param lastAttacker The L2Character that has killed the L2Attackable
*/
public void doItemDrop(L2Character lastAttacker)
{
L2PcInstance player = null;
if (lastAttacker instanceof L2PcInstance)
player = (L2PcInstance)lastAttacker;
else if (lastAttacker instanceof L2Summon)
player = ((L2Summon)lastAttacker).getOwner();
if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
int levelModifier = calculateLevelModifierForDrop(player);; // level modifier in %'s (will be subtracted from drop chance)
// Manage drop of Special Events created by GM for a defined period
doEventDrop(lastAttacker);
// Create a table containing all possible drops of this L2Attackable from L2NpcTemplate
List<L2DropData> drops = new FastList<L2DropData>();
drops.addAll(getTemplate().getDropData());
if (Config.DEBUG) _log.finer("this npc has "+drops.size()+" drops defined");
// Add Quest drops to the table containing all possible drops of this L2Attackable
player.fillQuestDrops(this, drops);
// Go Throw all possible drops (base + quests) of this L2Attackable
for (L2DropData drop : drops)
{
if (drop == null) continue;
// If it's a sweep drop, it will not appear on the ground
if (drop.isSweep()) continue;
dropItem(player, drop.getItemId(), calculateDropCount(drop, player, calculateDropCount(drop, player, levelModifier)));
}
}