Skip to content

Instantly share code, notes, and snippets.

@Battlecruiser
Last active August 29, 2015 13:57
Show Gist options
  • Save Battlecruiser/9682928 to your computer and use it in GitHub Desktop.
Save Battlecruiser/9682928 to your computer and use it in GitHub Desktop.
diff --git a/dist/game/config/General.properties b/dist/game/config/General.properties
index 46b2f78..8ff2722 100644
--- a/dist/game/config/General.properties
+++ b/dist/game/config/General.properties
@@ -339,7 +339,8 @@ ClearDroppedItemTable = False
# Default: False
AutoDeleteInvalidQuestData = False
-# Setting False can improve server performance on high rate/population servers.
+# If True, allows a special handling for drops when chance raises over 100% (eg. when applying chance rates).
+# True value causes better drop handling at higher rates.
# Default: True
PreciseDropCalculation = True
@@ -970,4 +971,4 @@ AltDevShowQuestsLoadInLogs = False
# Show scripts while loading them.
# Default: False
-AltDevShowScriptsLoadInLogs = False
\ No newline at end of file
+AltDevShowScriptsLoadInLogs = False
diff --git a/dist/game/config/NPC.properties b/dist/game/config/NPC.properties
index 6bc7483..420582d 100644
--- a/dist/game/config/NPC.properties
+++ b/dist/game/config/NPC.properties
@@ -182,6 +182,15 @@ MinionChaosTime = 10
# Drops
# ---------------------------------------------------------------------------
+# If True, activates bellow level gap rules for standard mobs:
+# Default: True
+UseDeepBlueDropRules = True
+
+# If True, activates bellow level gap rules for raid bosses:
+# Default: True
+UseDeepBlueDropRulesRaid = True
+
+
# The min and max level difference used for level gap calculation
# this is only for how many levels higher the player is than the monster
# Default: 8
@@ -206,4 +215,4 @@ DropItemMaxLevelDifference=10
# to allow dropping the item if level difference is bigger than DropAdenaMaxLevelDifference
# Note: This value is scalling from 100 to the specified value for DropAdenaMinLevelDifference to DropAdenaMaxLevelDifference limits
# Default: 10
-DropItemMinLevelGapChance=10
\ No newline at end of file
+DropItemMinLevelGapChance=10
diff --git a/dist/game/config/Rates.properties b/dist/game/config/Rates.properties
index 336cc36..c6e9eed 100644
--- a/dist/game/config/Rates.properties
+++ b/dist/game/config/Rates.properties
@@ -12,10 +12,12 @@
DeathDropAmountMultiplier = 1
CorpseDropAmountMultiplier = 1
HerbDropAmountMultiplier = 1
+RaidDropAmountMultiplier = 1
DeathDropChanceMultiplier = 1
CorpseDropChanceMultiplier = 1
HerbDropChanceMultiplier = 1
+RaidDropChanceMultiplier = 1
# List of items affected by custom drop rate by id, used now for Adena rate too.
# Usage: itemId1,multiplier1;itemId2,multiplier2;...
diff --git a/java/com/l2jserver/Config.java b/java/com/l2jserver/Config.java
index c24007f..f9e0047 100644
--- a/java/com/l2jserver/Config.java
+++ b/java/com/l2jserver/Config.java
@@ -1,18 +1,18 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -794,6 +794,8 @@ public final class Config
public static boolean ALT_ATTACKABLE_NPCS;
public static boolean ALT_GAME_VIEWNPC;
public static int MAX_DRIFT_RANGE;
+ public static boolean DEEPBLUE_DROP_RULES;
+ public static boolean DEEPBLUE_DROP_RULES_RAID;
public static boolean SHOW_NPC_LVL;
public static boolean SHOW_CREST_WITHOUT_QUEST;
public static boolean ENABLE_RANDOM_ENCHANT_EFFECT;
@@ -869,9 +871,11 @@ public final class Config
public static float RATE_DEATH_DROP_AMOUNT_MULTIPLIER;
public static float RATE_CORPSE_DROP_AMOUNT_MULTIPLIER;
public static float RATE_HERB_DROP_AMOUNT_MULTIPLIER;
+ public static float RATE_RAID_DROP_AMOUNT_MULTIPLIER;
public static float RATE_DEATH_DROP_CHANCE_MULTIPLIER;
public static float RATE_CORPSE_DROP_CHANCE_MULTIPLIER;
public static float RATE_HERB_DROP_CHANCE_MULTIPLIER;
+ public static float RATE_RAID_DROP_CHANCE_MULTIPLIER;
public static Map<Integer, Float> RATE_DROP_AMOUNT_MULTIPLIER;
public static Map<Integer, Float> RATE_DROP_CHANCE_MULTIPLIER;
public static float RATE_KARMA_LOST;
@@ -1835,6 +1839,7 @@ public final class Config
SAVE_DROPPED_ITEM_INTERVAL = General.getInt("SaveDroppedItemInterval", 60) * 60000;
CLEAR_DROPPED_ITEM_TABLE = General.getBoolean("ClearDroppedItemTable", false);
AUTODELETE_INVALID_QUEST_DATA = General.getBoolean("AutoDeleteInvalidQuestData", false);
+ PRECISE_DROP_CALCULATION = General.getBoolean("PreciseDropCalculation", true);
MULTIPLE_ITEM_DROP = General.getBoolean("MultipleItemDrop", true);
FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false);
LAZY_CACHE = General.getBoolean("LazyCache", true);
@@ -2025,6 +2030,8 @@ public final class Config
ALT_ATTACKABLE_NPCS = NPC.getBoolean("AltAttackableNpcs", true);
ALT_GAME_VIEWNPC = NPC.getBoolean("AltGameViewNpc", false);
MAX_DRIFT_RANGE = NPC.getInt("MaxDriftRange", 300);
+ DEEPBLUE_DROP_RULES = NPC.getBoolean("UseDeepBlueDropRules", true);
+ DEEPBLUE_DROP_RULES_RAID = NPC.getBoolean("UseDeepBlueDropRulesRaid", true);
SHOW_NPC_LVL = NPC.getBoolean("ShowNpcLevel", false);
SHOW_CREST_WITHOUT_QUEST = NPC.getBoolean("ShowCrestWithoutQuest", false);
ENABLE_RANDOM_ENCHANT_EFFECT = NPC.getBoolean("EnableRandomEnchantEffect", false);
@@ -2186,9 +2193,11 @@ public final class Config
RATE_DEATH_DROP_AMOUNT_MULTIPLIER = RatesSettings.getFloat("DeathDropAmountMultiplier", 1);
RATE_CORPSE_DROP_AMOUNT_MULTIPLIER = RatesSettings.getFloat("CorpseDropAmountMultiplier", 1);
RATE_HERB_DROP_AMOUNT_MULTIPLIER = RatesSettings.getFloat("HerbDropAmountMultiplier", 1);
+ RATE_RAID_DROP_AMOUNT_MULTIPLIER = RatesSettings.getFloat("RaidDropAmountMultiplier", 1);
RATE_DEATH_DROP_CHANCE_MULTIPLIER = RatesSettings.getFloat("DeathDropChanceMultiplier", 1);
RATE_CORPSE_DROP_CHANCE_MULTIPLIER = RatesSettings.getFloat("CorpseDropChanceMultiplier", 1);
RATE_HERB_DROP_CHANCE_MULTIPLIER = RatesSettings.getFloat("HerbDropChanceMultiplier", 1);
+ RATE_RAID_DROP_CHANCE_MULTIPLIER = RatesSettings.getFloat("RaidDropChanceMultiplier", 1);
String[] dropAmountMultiplier = RatesSettings.getString("DropAmountMultiplierByItemId", "").split(";");
RATE_DROP_AMOUNT_MULTIPLIER = new HashMap<>(dropAmountMultiplier.length);
if (!dropAmountMultiplier[0].isEmpty())
@@ -3022,6 +3031,9 @@ public final class Config
case "cleardroppeditemtable":
CLEAR_DROPPED_ITEM_TABLE = Boolean.parseBoolean(pValue);
break;
+ case "precisedropcalculation":
+ PRECISE_DROP_CALCULATION = Boolean.parseBoolean(pValue);
+ break;
case "multipleitemdrop":
MULTIPLE_ITEM_DROP = Boolean.parseBoolean(pValue);
break;
@@ -3168,6 +3180,12 @@ public final class Config
case "maxdriftrange":
MAX_DRIFT_RANGE = Integer.parseInt(pValue);
break;
+ case "usedeepbluedroprules":
+ DEEPBLUE_DROP_RULES = Boolean.parseBoolean(pValue);
+ break;
+ case "usedeepbluedroprulesraid":
+ DEEPBLUE_DROP_RULES_RAID = Boolean.parseBoolean(pValue);
+ break;
case "guardattackaggromob":
GUARD_ATTACK_AGGRO_MOB = Boolean.parseBoolean(pValue);
break;
diff --git a/java/com/l2jserver/gameserver/model/drops/CorpseDropItem.java b/java/com/l2jserver/gameserver/model/drops/CorpseDropItem.java
deleted file mode 100644
index efe0486..0000000
--- a/java/com/l2jserver/gameserver/model/drops/CorpseDropItem.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2004-2014 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * L2J Server is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model.drops;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.actor.L2Character;
-
-/**
- * @author Nos
- */
-public class CorpseDropItem extends GeneralDropItem
-{
- /**
- * @param itemId the item id
- * @param min the min count
- * @param max the max count
- * @param chance the chance of this drop item
- */
- public CorpseDropItem(int itemId, long min, long max, double chance)
- {
- super(itemId, min, max, chance);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getMin(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public long getMin(L2Character victim, L2Character killer)
- {
- return (long) (super.getMin(victim, killer) * Config.RATE_CORPSE_DROP_AMOUNT_MULTIPLIER);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getMax(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public long getMax(L2Character victim, L2Character killer)
- {
- return (long) (super.getMax(victim, killer) * Config.RATE_CORPSE_DROP_AMOUNT_MULTIPLIER);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getChance(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public double getChance(L2Character victim, L2Character killer)
- {
- return super.getChance(victim, killer) * Config.RATE_CORPSE_DROP_CHANCE_MULTIPLIER;
- }
-}
diff --git a/java/com/l2jserver/gameserver/model/drops/DeathDropItem.java b/java/com/l2jserver/gameserver/model/drops/DeathDropItem.java
deleted file mode 100644
index 43cb4a5..0000000
--- a/java/com/l2jserver/gameserver/model/drops/DeathDropItem.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2004-2014 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * L2J Server is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model.drops;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.actor.L2Character;
-
-/**
- * @author Nos
- */
-public class DeathDropItem extends GeneralDropItem
-{
- /**
- * @param itemId the item id
- * @param min the min count
- * @param max the max count
- * @param chance the chance of this drop item
- */
- public DeathDropItem(int itemId, long min, long max, double chance)
- {
- super(itemId, min, max, chance);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getMin(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public long getMin(L2Character victim, L2Character killer)
- {
- return (long) (super.getMin(victim, killer) * Config.RATE_DEATH_DROP_AMOUNT_MULTIPLIER);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getMax(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public long getMax(L2Character victim, L2Character killer)
- {
- return (long) (super.getMax(victim, killer) * Config.RATE_DEATH_DROP_AMOUNT_MULTIPLIER);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GeneralDropItem#getChance(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public double getChance(L2Character victim, L2Character killer)
- {
- return super.getChance(victim, killer) * Config.RATE_DEATH_DROP_CHANCE_MULTIPLIER;
- }
-}
diff --git a/java/com/l2jserver/gameserver/model/drops/DropListScope.java b/java/com/l2jserver/gameserver/model/drops/DropListScope.java
index 5e069b7..9597180 100644
--- a/java/com/l2jserver/gameserver/model/drops/DropListScope.java
+++ b/java/com/l2jserver/gameserver/model/drops/DropListScope.java
@@ -1,92 +1,63 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.model.drops;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import com.l2jserver.gameserver.model.drops.strategy.IAmountMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IChanceMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IGroupedItemDropCalculationStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IKillerChanceModifierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IPreciseDeterminationStrategy;
/**
* @author Nos
*/
-public enum DropListScope
+public enum DropListScope implements IDropItemFactory, IGroupedDropItemFactory
{
- DEATH(DeathDropItem.class, GroupedDeathDropItem.class),
- CORPSE(CorpseDropItem.class, GroupedCorpseDropItem.class);
+ DEATH((itemId, min, max, chance) -> new GeneralDropItem(itemId, min, max, chance, IAmountMultiplierStrategy.DROP, IChanceMultiplierStrategy.DROP), chance -> new GroupedGeneralDropItem(chance)),
+ CORPSE((itemId, min, max, chance) -> new GeneralDropItem(itemId, min, max, chance, IAmountMultiplierStrategy.SPOIL, IChanceMultiplierStrategy.SPOIL), DEATH),
- private static final Logger _log = Logger.getLogger(DropListScope.class.getName());
+ /**
+ * This droplist scope isn't affected by ANY rates, nor Champion, etc...
+ */
+ STATIC(
+ (itemId, min, max, chance) -> new GeneralDropItem(itemId, min, max, chance, IAmountMultiplierStrategy.STATIC, IChanceMultiplierStrategy.STATIC, IPreciseDeterminationStrategy.ALWAYS, IKillerChanceModifierStrategy.NO_RULES),
+ chance -> new GroupedGeneralDropItem(chance, IGroupedItemDropCalculationStrategy.DEFAULT_STRATEGY, IKillerChanceModifierStrategy.NO_RULES, IPreciseDeterminationStrategy.ALWAYS)),
+ QUEST((itemId, min, max, chance) -> new GeneralDropItem(itemId, min, max, chance, IAmountMultiplierStrategy.STATIC, IChanceMultiplierStrategy.QUEST, IPreciseDeterminationStrategy.ALWAYS, IKillerChanceModifierStrategy.NO_RULES), STATIC);
- private final Class<? extends GeneralDropItem> _dropItemClass;
- private final Class<? extends GroupedGeneralDropItem> _groupedDropItemClass;
+ private final IDropItemFactory _factory;
+ private final IGroupedDropItemFactory _groupFactory;
- private DropListScope(Class<? extends GeneralDropItem> dropItemClass, Class<? extends GroupedGeneralDropItem> groupedDropItemClass)
+ private DropListScope(IDropItemFactory factory, IGroupedDropItemFactory groupFactory)
{
- _dropItemClass = dropItemClass;
- _groupedDropItemClass = groupedDropItemClass;
+ _factory = factory;
+ _groupFactory = groupFactory;
}
+ @Override
public IDropItem newDropItem(int itemId, long min, long max, double chance)
{
- final Constructor<? extends GeneralDropItem> constructor;
- try
- {
- constructor = _dropItemClass.getConstructor(int.class, long.class, long.class, double.class);
- }
- catch (NoSuchMethodException | SecurityException e)
- {
- _log.log(Level.SEVERE, "Constructor(int, long, long, double) not found for " + _dropItemClass.getSimpleName(), e);
- return null;
- }
-
- try
- {
- return constructor.newInstance(itemId, min, max, chance);
- }
- catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
- {
- _log.log(Level.SEVERE, "", e);
- return null;
- }
+ return _factory.newDropItem(itemId, min, max, chance);
}
+ @Override
public GroupedGeneralDropItem newGroupedDropItem(double chance)
{
- final Constructor<? extends GroupedGeneralDropItem> constructor;
- try
- {
- constructor = _groupedDropItemClass.getConstructor(double.class);
- }
- catch (NoSuchMethodException | SecurityException e)
- {
- _log.log(Level.SEVERE, "Constructor(double) not found for " + _groupedDropItemClass.getSimpleName(), e);
- return null;
- }
-
- try
- {
- return constructor.newInstance(chance);
- }
- catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
- {
- _log.log(Level.SEVERE, "", e);
- return null;
- }
+ return _groupFactory.newGroupedDropItem(chance);
}
}
diff --git a/java/com/l2jserver/gameserver/model/drops/GeneralDropItem.java b/java/com/l2jserver/gameserver/model/drops/GeneralDropItem.java
index 467b2f3..87378bc 100644
--- a/java/com/l2jserver/gameserver/model/drops/GeneralDropItem.java
+++ b/java/com/l2jserver/gameserver/model/drops/GeneralDropItem.java
@@ -1,43 +1,50 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.model.drops;
-import java.util.ArrayList;
import java.util.List;
-import com.l2jserver.Config;
import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.strategy.IAmountMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IChanceMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IDropCalculationStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IKillerChanceModifierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.INonGroupedKillerChanceModifierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IPreciseDeterminationStrategy;
import com.l2jserver.gameserver.model.holders.ItemHolder;
-import com.l2jserver.gameserver.model.itemcontainer.Inventory;
-import com.l2jserver.gameserver.util.Util;
-import com.l2jserver.util.Rnd;
/**
* @author Nos
*/
-public class GeneralDropItem implements IDropItem
+public final class GeneralDropItem implements IDropItem
{
private final int _itemId;
private final long _min;
private final long _max;
private final double _chance;
+ protected final IAmountMultiplierStrategy _amountStrategy;
+ protected final IChanceMultiplierStrategy _chanceStrategy;
+ protected final IPreciseDeterminationStrategy _preciseStrategy;
+ protected final INonGroupedKillerChanceModifierStrategy _killerStrategy;
+ protected final IDropCalculationStrategy _dropCalculationStrategy;
+
/**
* @param itemId the item id
* @param min the min count
@@ -46,105 +53,155 @@ public class GeneralDropItem implements IDropItem
*/
public GeneralDropItem(int itemId, long min, long max, double chance)
{
+ this(itemId, min, max, chance, 1, 1);
+ }
+
+ public GeneralDropItem(int itemId, long min, long max, double chance, double defaultAmountMultiplier, double defaultChanceMultiplier)
+ {
+ this(itemId, min, max, defaultChanceMultiplier, IAmountMultiplierStrategy.DEFAULT_STRATEGY(defaultAmountMultiplier), IChanceMultiplierStrategy.DEFAULT_STRATEGY(defaultChanceMultiplier));
+ }
+
+ public GeneralDropItem(int itemId, long min, long max, double chance, IAmountMultiplierStrategy amountMultiplierStrategy, IChanceMultiplierStrategy chanceMultiplierStrategy)
+ {
+ this(itemId, min, max, chance, amountMultiplierStrategy, chanceMultiplierStrategy, IPreciseDeterminationStrategy.DEFAULT, IKillerChanceModifierStrategy.DEFAULT_NONGROUP_STRATEGY);
+ }
+
+ public GeneralDropItem(int itemId, long min, long max, double chance, IAmountMultiplierStrategy amountMultiplierStrategy, IChanceMultiplierStrategy chanceMultiplierStrategy, IPreciseDeterminationStrategy preciseStrategy, INonGroupedKillerChanceModifierStrategy killerStrategy)
+ {
+ this(itemId, min, max, chance, amountMultiplierStrategy, chanceMultiplierStrategy, preciseStrategy, killerStrategy, IDropCalculationStrategy.DEFAULT_STRATEGY);
+ }
+
+ public GeneralDropItem(int itemId, long min, long max, double chance, IAmountMultiplierStrategy amountMultiplierStrategy, IChanceMultiplierStrategy chanceMultiplierStrategy, IPreciseDeterminationStrategy preciseStrategy, INonGroupedKillerChanceModifierStrategy killerStrategy, IDropCalculationStrategy dropCalculationStrategy)
+ {
_itemId = itemId;
_min = min;
_max = max;
_chance = chance;
+ _amountStrategy = amountMultiplierStrategy;
+ _chanceStrategy = chanceMultiplierStrategy;
+ _preciseStrategy = preciseStrategy;
+ _killerStrategy = killerStrategy;
+ _dropCalculationStrategy = dropCalculationStrategy;
+
+ }
+
+ /**
+ * @return the _amountStrategy
+ */
+ public final IAmountMultiplierStrategy getAmountStrategy()
+ {
+ return _amountStrategy;
+ }
+
+ /**
+ * @return the _chanceStrategy
+ */
+ public final IChanceMultiplierStrategy getChanceStrategy()
+ {
+ return _chanceStrategy;
+ }
+
+ /**
+ * @return the _preciseStrategy
+ */
+ public final IPreciseDeterminationStrategy getPreciseStrategy()
+ {
+ return _preciseStrategy;
+ }
+
+ /**
+ * @return the _killerStrategy
+ */
+ public final INonGroupedKillerChanceModifierStrategy getKillerChanceModifierStrategy()
+ {
+ return _killerStrategy;
+ }
+
+ /**
+ * @return the _dropCalculationStrategy
+ */
+ public final IDropCalculationStrategy getDropCalculationStrategy()
+ {
+ return _dropCalculationStrategy;
}
/**
* Gets the item id
* @return the item id
*/
- public int getItemId()
+ public final int getItemId()
{
return _itemId;
}
/**
- * Gets the min drop count
+ * Gets the base min drop count
* @return the min
*/
- public long getMin()
+ public final long getMin()
{
return _min;
}
/**
- * Gets the min drop count
- * @param victim the victim
- * @param killer the killer
+ * Gets the min drop count modified by server rates
+ * @param victim the victim who drops the item
* @return the min modified by any rates.
*/
- public long getMin(L2Character victim, L2Character killer)
+ public final long getMin(L2Character victim)
{
- double multiplier = 1;
- if (victim.isChampion())
- {
- multiplier *= getItemId() != Inventory.ADENA_ID ? Config.L2JMOD_CHAMPION_REWARDS : Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
- }
- Float dropChanceMultiplier = Config.RATE_DROP_AMOUNT_MULTIPLIER.get(getItemId());
- if (dropChanceMultiplier != null)
- {
- multiplier *= dropChanceMultiplier;
- }
- return (long) (getMin() * multiplier);
+ return (long) (getMin() * getAmountMultiplier(victim));
}
/**
- * Gets the max drop count
+ * Gets the base max drop count
* @return the max
*/
- public long getMax()
+ public final long getMax()
{
return _max;
}
/**
- * Gets the max drop count
- * @param victim the victim
- * @param killer the killer
+ * Gets the max drop count modified by server rates
+ * @param victim the victim who drops the item
* @return the max modified by any rates.
*/
- public long getMax(L2Character victim, L2Character killer)
+ public final long getMax(L2Character victim)
{
- double multiplier = 1;
- if (victim.isChampion())
- {
- multiplier *= getItemId() != Inventory.ADENA_ID ? Config.L2JMOD_CHAMPION_REWARDS : Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
- }
- Float dropChanceMultiplier = Config.RATE_DROP_AMOUNT_MULTIPLIER.get(getItemId());
- if (dropChanceMultiplier != null)
- {
- multiplier *= dropChanceMultiplier;
- }
- return (long) (getMax() * multiplier);
+ return (long) (getMax() * getAmountMultiplier(victim));
}
/**
* Gets the chance of this drop item.
* @return the chance
*/
- public double getChance()
+ public final double getChance()
{
return _chance;
}
/**
- * Gets the chance of this drop item.
- * @param victim the victim
- * @param killer the killer
+ * Gets the general chance to drop this item modified by rates. <br>
+ * This shall be used in calculating chance within drop groups.
+ * @param victim the victim who drops the item
* @return the chance modified by any rates.
*/
- public double getChance(L2Character victim, L2Character killer)
+ public final double getChance(L2Character victim)
{
- float multiplier = 1;
- Float dropChanceMultiplier = Config.RATE_DROP_CHANCE_MULTIPLIER.get(getItemId());
- if (dropChanceMultiplier != null)
- {
- multiplier *= dropChanceMultiplier;
- }
- return getChance() * multiplier;
+ return getChance() * getChanceMultiplier(victim);
+ }
+
+ /**
+ * Gets the chance of dropping this item for current killer and victim (modified by server rates and another rules based on killer) <br>
+ * This shall be used to calculate chance outside of drop groups.
+ * @param victim the victim who drops the item
+ * @param killer who kills the victim
+ * @return a chance to drop modified by deep blue drop rules
+ */
+ public final double getChance(L2Character victim, L2Character killer)
+ {
+ return (getKillerChanceModifier(victim, killer) * getChance(victim));
}
/*
@@ -152,34 +209,47 @@ public class GeneralDropItem implements IDropItem
* @see com.l2jserver.gameserver.model.drop.IDropItem#calculateDrops(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
*/
@Override
- public List<ItemHolder> calculateDrops(L2Character victim, L2Character killer)
- {
- final int levelDifference = victim.getLevel() - killer.getLevel();
- final double levelGapChanceToDrop;
- if (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);
- }
-
- // There is a chance of level gap that it wont drop this item
- if (levelGapChanceToDrop < (Rnd.nextDouble() * 100))
- {
- return null;
- }
-
- if (getChance(victim, killer) > (Rnd.nextDouble() * 100))
- {
- final long amount = Rnd.get(getMin(victim, killer), getMax(victim, killer));
-
- final List<ItemHolder> items = new ArrayList<>(1);
- items.add(new ItemHolder(getItemId(), amount));
- return items;
- }
-
- return null;
+ public final List<ItemHolder> calculateDrops(L2Character victim, L2Character killer)
+ {
+ return _dropCalculationStrategy.calculateDrops(this, victim, killer);
+ }
+
+ /**
+ * @return <code>true</code> if chance over 100% should be handled
+ */
+ public final boolean isPreciseCalculated()
+ {
+ return _preciseStrategy.isPreciseCalculated(this);
+ }
+
+ /**
+ * This handles by default deep blue drop rules. It may also be used to handle another drop chance rules based on killer
+ * @param victim the victim who drops the item
+ * @param killer who kills the victim
+ * @return a number between 0 and 1 (usually)
+ */
+ protected final double getKillerChanceModifier(L2Character victim, L2Character killer)
+ {
+ return _killerStrategy.getKillerChanceModifier(this, victim, killer);
+ }
+
+ /**
+ * This gets standard server rates for this item
+ * @param victim who drops the item
+ * @return
+ */
+ protected final double getAmountMultiplier(L2Character victim)
+ {
+ return _amountStrategy.getAmountMultiplier(this, victim);
+ }
+
+ /**
+ * This gets standard server rates for this item
+ * @param victim who drops the item
+ * @return
+ */
+ protected final double getChanceMultiplier(L2Character victim)
+ {
+ return _chanceStrategy.getChanceMultiplier(this, victim);
}
}
diff --git a/java/com/l2jserver/gameserver/model/drops/GroupedCorpseDropItem.java b/java/com/l2jserver/gameserver/model/drops/GroupedCorpseDropItem.java
deleted file mode 100644
index 6c217d3..0000000
--- a/java/com/l2jserver/gameserver/model/drops/GroupedCorpseDropItem.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2004-2014 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * L2J Server is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model.drops;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.actor.L2Character;
-
-/**
- * @author Nos
- */
-public class GroupedCorpseDropItem extends GroupedGeneralDropItem
-{
- /**
- * @param chance the chance of this drop item.
- */
- public GroupedCorpseDropItem(double chance)
- {
- super(chance);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem#getChance(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public double getChance(L2Character victim, L2Character killer)
- {
- return super.getChance(victim, killer) * Config.RATE_CORPSE_DROP_CHANCE_MULTIPLIER;
- }
-}
diff --git a/java/com/l2jserver/gameserver/model/drops/GroupedDeathDropItem.java b/java/com/l2jserver/gameserver/model/drops/GroupedDeathDropItem.java
deleted file mode 100644
index 90996e2..0000000
--- a/java/com/l2jserver/gameserver/model/drops/GroupedDeathDropItem.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2004-2014 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * L2J Server is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package com.l2jserver.gameserver.model.drops;
-
-import com.l2jserver.Config;
-import com.l2jserver.gameserver.model.actor.L2Character;
-
-/**
- * @author Nos
- */
-public class GroupedDeathDropItem extends GroupedGeneralDropItem
-{
- /**
- * @param chance the chance of this drop item.
- */
- public GroupedDeathDropItem(double chance)
- {
- super(chance);
- }
-
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem#getChance(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
- */
- @Override
- public double getChance(L2Character victim, L2Character killer)
- {
- return super.getChance(victim, killer) * Config.RATE_DEATH_DROP_CHANCE_MULTIPLIER;
- }
-}
diff --git a/java/com/l2jserver/gameserver/model/drops/GroupedGeneralDropItem.java b/java/com/l2jserver/gameserver/model/drops/GroupedGeneralDropItem.java
index e14fa31..590ac7d 100644
--- a/java/com/l2jserver/gameserver/model/drops/GroupedGeneralDropItem.java
+++ b/java/com/l2jserver/gameserver/model/drops/GroupedGeneralDropItem.java
@@ -1,18 +1,18 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -22,110 +22,235 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import com.l2jserver.Config;
import com.l2jserver.gameserver.model.actor.L2Character;
-import com.l2jserver.gameserver.model.actor.instance.L2RaidBossInstance;
+import com.l2jserver.gameserver.model.drops.strategy.IAmountMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IChanceMultiplierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IGroupedItemDropCalculationStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IKillerChanceModifierStrategy;
+import com.l2jserver.gameserver.model.drops.strategy.IPreciseDeterminationStrategy;
import com.l2jserver.gameserver.model.holders.ItemHolder;
-import com.l2jserver.gameserver.util.Util;
-import com.l2jserver.util.Rnd;
/**
* @author Nos
*/
-public class GroupedGeneralDropItem implements IDropItem
+public final class GroupedGeneralDropItem implements IDropItem
{
+
private final double _chance;
private List<GeneralDropItem> _items;
+ protected final IGroupedItemDropCalculationStrategy _dropCalculationStrategy;
+ protected final IKillerChanceModifierStrategy _killerChanceModifierStrategy;
+ protected final IPreciseDeterminationStrategy _preciseStrategy;
/**
* @param chance the chance of this drop item.
*/
public GroupedGeneralDropItem(double chance)
{
- _chance = chance;
+ this(chance, IGroupedItemDropCalculationStrategy.DEFAULT_STRATEGY, IKillerChanceModifierStrategy.DEFAULT_STRATEGY, IPreciseDeterminationStrategy.DEFAULT);
}
/**
- * Gets the chance of this drop item.
- * @return the chance
+ * @param chance the chance of this drop item.
+ * @param dropStrategy to calculate drops.
+ * @param killerStrategy
+ * @param preciseStrategy
*/
- public double getChance()
+ public GroupedGeneralDropItem(double chance, IGroupedItemDropCalculationStrategy dropStrategy, IKillerChanceModifierStrategy killerStrategy, IPreciseDeterminationStrategy preciseStrategy)
{
- return _chance;
+ _chance = chance;
+ _dropCalculationStrategy = dropStrategy;
+ _killerChanceModifierStrategy = killerStrategy;
+ _preciseStrategy = preciseStrategy;
}
/**
* Gets the chance of this drop item.
- * @param victim the victim
- * @param killer the killer
- * @return the chance modified by any rates.
+ * @return the chance
*/
- public double getChance(L2Character victim, L2Character killer)
+ public final double getChance()
{
- return getChance();
+ return _chance;
}
/**
* Gets the items.
* @return the items
*/
- public List<GeneralDropItem> getItems()
+ public final List<GeneralDropItem> getItems()
{
return _items;
}
/**
+ * @return the strategy
+ */
+ public final IGroupedItemDropCalculationStrategy getDropCalculationStrategy()
+ {
+ return _dropCalculationStrategy;
+ }
+
+ /**
+ * @return the _killerChanceModifierStrategy
+ */
+ public IKillerChanceModifierStrategy getKillerChanceModifierStrategy()
+ {
+ return _killerChanceModifierStrategy;
+ }
+
+ /**
+ * @return the _preciseStrategy
+ */
+ public final IPreciseDeterminationStrategy getPreciseStrategy()
+ {
+ return _preciseStrategy;
+ }
+
+ /**
* Sets an item list to this drop item.
* @param items the item list
*/
- public void setItems(List<GeneralDropItem> items)
+ public final void setItems(List<GeneralDropItem> items)
{
_items = Collections.unmodifiableList(items);
}
- /*
- * (non-Javadoc)
- * @see com.l2jserver.gameserver.model.drop.IDropItem#calculateDrops(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
+ /**
+ * Returns a list of items in the group with chance multiplied by chance of the group
+ * @return the list of items with modified chances
*/
- @Override
- public List<ItemHolder> calculateDrops(L2Character victim, L2Character killer)
+ public final List<GeneralDropItem> extractMe()
{
- int levelDifference = victim.getLevel() - killer.getLevel();
- double chanceModifier;
- if (victim instanceof L2RaidBossInstance)
+ List<GeneralDropItem> items = new ArrayList<>();
+ for (final GeneralDropItem item : getItems())
{
- chanceModifier = Math.max(0, Math.min(1, (levelDifference * 0.15) + 1));
+ // precise and killer strategies of the group
+ items.add(new GeneralDropItem(item.getItemId(), item.getMin(), item.getMax(), (item.getChance() * getChance()) / 100, item.getAmountStrategy(), item.getChanceStrategy(), getPreciseStrategy(), getKillerChanceModifierStrategy(), item.getDropCalculationStrategy()));
}
- else
+ return items;
+ }
+
+ /**
+ * statically normalizes a group, useful when need to convert legacy SQL data
+ * @return a new group with items, which have a sum of getChance() of 100%
+ */
+ public final GroupedGeneralDropItem normalizeMe()
+ {
+ double sumchance = 0;
+ for (GeneralDropItem item : getItems())
{
- chanceModifier = 1;
-
- double 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);
- // There is a chance of level gap that it wont drop this item
- if (levelGapChanceToDrop < (Rnd.nextDouble() * 100))
- {
- return null;
- }
+ sumchance += (item.getChance() * getChance()) / 100;
}
-
- if ((getChance(victim, killer) * chanceModifier) > (Rnd.nextDouble() * 100))
+ final double sumchance1 = sumchance;
+ GroupedGeneralDropItem group = new GroupedGeneralDropItem(sumchance1, getDropCalculationStrategy(), IKillerChanceModifierStrategy.NO_RULES, getPreciseStrategy());
+ List<GeneralDropItem> items = new ArrayList<>();
+ for (final GeneralDropItem item : getItems())
{
- double random = (Rnd.nextDouble() * 100);
- double totalChance = 0;
- for (GeneralDropItem item : getItems())
- {
- // Grouped item chance rates should not be modified.
- totalChance += item.getChance();
- if (totalChance > random)
- {
- long amount = Rnd.get(item.getMin(victim, killer), item.getMax(victim, killer));
-
- List<ItemHolder> items = new ArrayList<>(1);
- items.add(new ItemHolder(item.getItemId(), amount));
- return items;
- }
- }
+ // modify only the chance, leave all other rules intact
+ items.add(new GeneralDropItem(item.getItemId(), item.getMin(), item.getMax(), (item.getChance() * getChance()) / sumchance1, item.getAmountStrategy(), item.getChanceStrategy(), item.getPreciseStrategy(), item.getKillerChanceModifierStrategy(), item.getDropCalculationStrategy()));
}
- return null;
+ group.setItems(items);
+ return group;
+ }
+
+ /**
+ * Creates a normalized group taking into account all drop modifiers, needed when handling a group which has items with different chance rates
+ * @param victim
+ * @param killer
+ * @return a new normalized group with all drop modifiers applied
+ */
+ public final GroupedGeneralDropItem normalizeMe(L2Character victim, L2Character killer)
+ {
+ return normalizeMe(victim, killer, true, 1);
+ }
+
+ /**
+ * Creates a normalized group taking into account all drop modifiers, needed when handling a group which has items with different chance rates
+ * @param victim
+ * @param killer
+ * @param chanceModifier an additional chance modifier
+ * @return a new normalized group with all drop modifiers applied
+ */
+ public final GroupedGeneralDropItem normalizeMe(L2Character victim, L2Character killer, double chanceModifier)
+ {
+ return normalizeMe(victim, killer, true, chanceModifier);
+ }
+
+ /**
+ * Creates a normalized group taking into account all drop modifiers, needed when handling a group which has items with different chance rates
+ * @param victim
+ * @return a new normalized group with all victim modifiers applied
+ */
+ public final GroupedGeneralDropItem normalizeMe(L2Character victim)
+ {
+ return normalizeMe(victim, null, false, 1);
+ }
+
+ /**
+ * Creates a normalized group taking into account all drop modifiers, needed when handling a group which has items with different chance rates
+ * @param victim
+ * @param chanceModifier an additional chance modifier
+ * @return a new normalized group with all victim modifiers applied
+ */
+ public final GroupedGeneralDropItem normalizeMe(L2Character victim, double chanceModifier)
+ {
+ return normalizeMe(victim, null, false, chanceModifier);
+ }
+
+ /**
+ * Creates a normalized group taking into account all drop modifiers, needed when handling a group which has items with different chance rates
+ * @param victim
+ * @param killer
+ * @param applyKillerModifier if to modify chance by {@link GroupedGeneralDropItem#getKillerChanceModifier(L2Character, L2Character)}
+ * @param chanceModifier an additional chance modifier
+ * @return a new normalized group with all drop modifiers applied
+ */
+ private final GroupedGeneralDropItem normalizeMe(L2Character victim, L2Character killer, boolean applyKillerModifier, double chanceModifier)
+ {
+ if (applyKillerModifier)
+ {
+ chanceModifier *= (getKillerChanceModifier(victim, killer));
+ }
+ double sumchance = 0;
+ for (GeneralDropItem item : getItems())
+ {
+ sumchance += (item.getChance(victim) * getChance() * chanceModifier) / 100;
+ }
+ GroupedGeneralDropItem group = new GroupedGeneralDropItem(sumchance, getDropCalculationStrategy(), IKillerChanceModifierStrategy.NO_RULES, getPreciseStrategy()); // to discard further deep blue calculations
+ List<GeneralDropItem> items = new ArrayList<>();
+ for (GeneralDropItem item : getItems())
+ {
+ // the item is made almost "static"
+ items.add(new GeneralDropItem(item.getItemId(), item.getMin(victim), item.getMax(victim), (item.getChance(victim) * getChance() * chanceModifier) / sumchance, IAmountMultiplierStrategy.STATIC, IChanceMultiplierStrategy.STATIC, getPreciseStrategy(), IKillerChanceModifierStrategy.NO_RULES, item.getDropCalculationStrategy()));
+ }
+ group.setItems(items);
+ return group;
+
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.l2jserver.gameserver.model.drop.IDropItem#calculateDrops(com.l2jserver.gameserver.model.actor.L2Character, com.l2jserver.gameserver.model.actor.L2Character)
+ */
+ @Override
+ public final List<ItemHolder> calculateDrops(L2Character victim, L2Character killer)
+ {
+ return _dropCalculationStrategy.calculateDrops(this, victim, killer);
+ }
+
+ /**
+ * This handles by default deep blue drop rules. It may also be used to handle another drop chance rules based on killer
+ * @param victim the victim who drops the item
+ * @param killer who kills the victim
+ * @return a number between 0 and 1 (usually)
+ */
+ public final double getKillerChanceModifier(L2Character victim, L2Character killer)
+ {
+ return _killerChanceModifierStrategy.getKillerChanceModifier(this, victim, killer);
+ }
+
+ public boolean isPreciseCalculated()
+ {
+ return _preciseStrategy.isPreciseCalculated(this);
}
}
diff --git a/java/com/l2jserver/gameserver/model/drops/IDropItem.java b/java/com/l2jserver/gameserver/model/drops/IDropItem.java
index 58d9b93..5f84a9c 100644
--- a/java/com/l2jserver/gameserver/model/drops/IDropItem.java
+++ b/java/com/l2jserver/gameserver/model/drops/IDropItem.java
@@ -28,6 +28,7 @@ import com.l2jserver.gameserver.model.holders.ItemHolder;
*/
public interface IDropItem
{
+
/**
* Calculates drops of this drop item.
* @param victim the victim
diff --git a/java/com/l2jserver/gameserver/model/drops/IDropItemFactory.java b/java/com/l2jserver/gameserver/model/drops/IDropItemFactory.java
new file mode 100644
index 0000000..f5c0133
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/IDropItemFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IDropItemFactory
+{
+ /**
+ * @param itemId the item id
+ * @param min the min count
+ * @param max the max count
+ * @param chance the chance of this drop item
+ * @return the drop item created by this factory
+ */
+ public IDropItem newDropItem(int itemId, long min, long max, double chance);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/IGroupedDropItemFactory.java b/java/com/l2jserver/gameserver/model/drops/IGroupedDropItemFactory.java
new file mode 100644
index 0000000..878529c
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/IGroupedDropItemFactory.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IGroupedDropItemFactory
+{
+ public GroupedGeneralDropItem newGroupedDropItem(double chance);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IAmountMultiplierStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IAmountMultiplierStrategy.java
new file mode 100644
index 0000000..afa261a
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IAmountMultiplierStrategy.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.itemcontainer.Inventory;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IAmountMultiplierStrategy
+{
+ public static final IAmountMultiplierStrategy DROP = DEFAULT_STRATEGY(Config.RATE_DEATH_DROP_AMOUNT_MULTIPLIER);
+ public static final IAmountMultiplierStrategy SPOIL = DEFAULT_STRATEGY(Config.RATE_CORPSE_DROP_AMOUNT_MULTIPLIER);
+ public static final IAmountMultiplierStrategy STATIC = (item, victim) -> 1;
+
+ public static IAmountMultiplierStrategy DEFAULT_STRATEGY(final double defaultMultiplier)
+ {
+ return (item, victim) ->
+ {
+ double multiplier = 1;
+ if (victim.isChampion())
+ {
+ multiplier *= item.getItemId() != Inventory.ADENA_ID ? Config.L2JMOD_CHAMPION_REWARDS : Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
+ }
+ Float dropChanceMultiplier = Config.RATE_DROP_AMOUNT_MULTIPLIER.get(item.getItemId());
+ if (dropChanceMultiplier != null)
+ {
+ multiplier *= dropChanceMultiplier;
+ }
+ else if (ItemTable.getInstance().getTemplate(item.getItemId()).hasExImmediateEffect())
+ {
+ multiplier *= Config.RATE_HERB_DROP_AMOUNT_MULTIPLIER;
+ }
+ else if (victim.isRaid())
+ {
+ multiplier *= Config.RATE_RAID_DROP_AMOUNT_MULTIPLIER;
+ }
+ else
+ {
+ multiplier *= defaultMultiplier;
+ }
+ return multiplier;
+ };
+ }
+
+ public double getAmountMultiplier(GeneralDropItem item, L2Character victim);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IChanceMultiplierStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IChanceMultiplierStrategy.java
new file mode 100644
index 0000000..ccc6031
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IChanceMultiplierStrategy.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.datatables.ItemTable;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.itemcontainer.Inventory;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IChanceMultiplierStrategy
+{
+ public static final IChanceMultiplierStrategy DROP = DEFAULT_STRATEGY(Config.RATE_DEATH_DROP_CHANCE_MULTIPLIER);
+ public static final IChanceMultiplierStrategy SPOIL = DEFAULT_STRATEGY(Config.RATE_CORPSE_DROP_CHANCE_MULTIPLIER);
+ public static final IChanceMultiplierStrategy STATIC = (item, victim) -> 1;
+
+ public static final IChanceMultiplierStrategy QUEST = (item, victim) ->
+ {
+ double championmult;
+ if ((item.getItemId() == Inventory.ADENA_ID) || (item.getItemId() == Inventory.ANCIENT_ADENA_ID))
+ {
+ championmult = Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
+ }
+ else
+ {
+ championmult = Config.L2JMOD_CHAMPION_REWARDS;
+ }
+
+ return (Config.L2JMOD_CHAMPION_ENABLE && (victim != null) && victim.isChampion()) ? (Config.RATE_QUEST_DROP * championmult) : Config.RATE_QUEST_DROP;
+ };
+
+ public static IChanceMultiplierStrategy DEFAULT_STRATEGY(final double defaultMultiplier)
+ {
+ return (item, victim) ->
+ {
+ float multiplier = 1;
+ Float dropChanceMultiplier = Config.RATE_DROP_CHANCE_MULTIPLIER.get(item.getItemId());
+ if (dropChanceMultiplier != null)
+ {
+ multiplier *= dropChanceMultiplier;
+ }
+ else if (ItemTable.getInstance().getTemplate(item.getItemId()).hasExImmediateEffect())
+ {
+ multiplier *= Config.RATE_HERB_DROP_CHANCE_MULTIPLIER;
+ }
+ else if (victim.isRaid())
+ {
+ multiplier *= Config.RATE_RAID_DROP_CHANCE_MULTIPLIER;
+ }
+ else
+ {
+ multiplier *= defaultMultiplier;
+ }
+ return multiplier;
+ };
+ }
+
+ public double getChanceMultiplier(GeneralDropItem item, L2Character victim);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IDropCalculationStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IDropCalculationStrategy.java
new file mode 100644
index 0000000..34c1b35
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IDropCalculationStrategy.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import java.util.Collections;
+import java.util.List;
+
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.holders.ItemHolder;
+import com.l2jserver.util.Rnd;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IDropCalculationStrategy
+{
+ public static final IDropCalculationStrategy DEFAULT_STRATEGY = (item, victim, killer) ->
+ {
+ if (item.getChance(victim, killer) > (Rnd.nextDouble() * 100))
+ {
+ int amountMultiply = 1;
+ if (item.isPreciseCalculated() && (item.getChance(victim, killer) > 100))
+ {
+ amountMultiply = (int) item.getChance(victim, killer) / 100;
+ if ((item.getChance(victim, killer) % 100) > (Rnd.nextDouble() * 100))
+ {
+ amountMultiply++;
+ }
+ }
+
+ long amount = Rnd.get(item.getMin(victim) * amountMultiply, item.getMax(victim) * amountMultiply);
+
+ return Collections.singletonList(new ItemHolder(item.getItemId(), amount));
+ }
+
+ return null;
+ };
+
+ public List<ItemHolder> calculateDrops(GeneralDropItem item, L2Character victim, L2Character killer);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IGroupedItemDropCalculationStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IGroupedItemDropCalculationStrategy.java
new file mode 100644
index 0000000..20ddf53
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IGroupedItemDropCalculationStrategy.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem;
+import com.l2jserver.gameserver.model.drops.IDropItem;
+import com.l2jserver.gameserver.model.holders.ItemHolder;
+import com.l2jserver.util.Rnd;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IGroupedItemDropCalculationStrategy
+{
+ /**
+ * The default strategy used in L2J to calculate drops. When the group's chance raises over 100% and group has precise calculation, the dropped item's amount increases.
+ */
+ public static final IGroupedItemDropCalculationStrategy DEFAULT_STRATEGY = (dropItem, victim, killer) ->
+ {
+ if (dropItem.getItems().size() == 1)
+ {
+
+ final GeneralDropItem item1 = dropItem.getItems().iterator().next();
+ return new GeneralDropItem(item1.getItemId(), item1.getMin(), item1.getMax(), (item1.getChance() * dropItem.getChance()) / 100, item1.getAmountStrategy(), item1.getChanceStrategy(), dropItem.getPreciseStrategy(), dropItem.getKillerChanceModifierStrategy(), item1.getDropCalculationStrategy()).calculateDrops(victim, killer);
+ }
+
+ GroupedGeneralDropItem normalized = dropItem.normalizeMe(victim, killer);
+ if (normalized.getChance() > (Rnd.nextDouble() * 100))
+ {
+ double random = (Rnd.nextDouble() * 100);
+ double totalChance = 0;
+ for (GeneralDropItem item2 : normalized.getItems())
+ {
+ // Grouped item chance rates should not be modified (the whole magic was already done by normalizing thus the items' chance sum is always 100%).
+ totalChance += item2.getChance();
+ if (totalChance > random)
+ {
+ int amountMultiply = 1;
+ if (dropItem.isPreciseCalculated() && (normalized.getChance() >= 100))
+ {
+ amountMultiply = (int) (normalized.getChance()) / 100;
+ if ((normalized.getChance() % 100) > (Rnd.nextDouble() * 100))
+ {
+ amountMultiply++;
+ }
+ }
+
+ long amount = Rnd.get(item2.getMin(victim) * amountMultiply, item2.getMax(victim) * amountMultiply);
+
+ return Collections.singletonList(new ItemHolder(item2.getItemId(), amount));
+ }
+ }
+ }
+ return null;
+ };
+
+ /**
+ * This strategy calculates a group's drop by calculating drops of its individual items and merging its results.
+ */
+ public static final IGroupedItemDropCalculationStrategy DISBAND_GROUP = (item, victim, killer) ->
+ {
+ List<ItemHolder> dropped = new ArrayList<>();
+ for (IDropItem dropItem : item.extractMe())
+ {
+ dropped.addAll(dropItem.calculateDrops(victim, killer));
+ }
+ return dropped.isEmpty() ? null : dropped;
+ };
+
+ /**
+ * This strategy when group has precise calculation rolls multiple times over group to determine drops when group's chance raises over 100% instead of just multiplying the dropped item's amount. Thus it can produce different items from group at once.
+ */
+ public static final IGroupedItemDropCalculationStrategy PRECISE_MULTIPLE_GROUP_ROLLS = (item, victim, killer) ->
+ {
+ if (!item.isPreciseCalculated())
+ {
+ // if item hasn't precise calculation there's no change from DEFAULT_STRATEGY
+ return DEFAULT_STRATEGY.calculateDrops(item, victim, victim);
+ }
+ GroupedGeneralDropItem newItem = new GroupedGeneralDropItem(item.getChance(), DEFAULT_STRATEGY, item.getKillerChanceModifierStrategy(), IPreciseDeterminationStrategy.NEVER);
+ newItem.setItems(item.getItems());
+ GroupedGeneralDropItem normalized = newItem.normalizeMe(victim, killer);
+ // Let's determine the number of rolls.
+ int rolls = (int) (normalized.getChance() / 100);
+ if ((Rnd.nextDouble() * 100) < (normalized.getChance() % 100))
+ {
+ rolls++;
+ }
+ List<ItemHolder> dropped = new ArrayList<>();
+ for (; rolls > 0; rolls--)
+ {
+ // As further normalizing on already normalized drop group does nothing, we can just pass the calculation to DEFAULT_STRATEGY with precise calculation disabled as we handle it.
+ dropped.addAll(normalized.calculateDrops(victim, killer));
+ }
+ return dropped.isEmpty() ? null : dropped;
+ };
+
+ public List<ItemHolder> calculateDrops(GroupedGeneralDropItem item, L2Character victim, L2Character killer);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IKillerChanceModifierStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IKillerChanceModifierStrategy.java
new file mode 100644
index 0000000..023e462
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IKillerChanceModifierStrategy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.drops.IDropItem;
+import com.l2jserver.gameserver.model.itemcontainer.Inventory;
+import com.l2jserver.gameserver.util.Util;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IKillerChanceModifierStrategy extends INonGroupedKillerChanceModifierStrategy
+{
+ public static final IKillerChanceModifierStrategy DEFAULT_STRATEGY = (item, victim, killer) ->
+ {
+ int levelDifference = victim.getLevel() - killer.getLevel();
+ if ((victim.isRaid()) && Config.DEEPBLUE_DROP_RULES_RAID)
+ {
+ // FIXME: Config?
+ return Math.max(0, Math.min(1, (levelDifference * 0.15) + 1));
+ }
+ else if (Config.DEEPBLUE_DROP_RULES)
+ {
+
+ return Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0) / 100;
+ }
+ return 1;
+ };
+ public static final INonGroupedKillerChanceModifierStrategy DEFAULT_NONGROUP_STRATEGY = (item, victim, killer) ->
+ {
+ if (((!(victim.isRaid())) && Config.DEEPBLUE_DROP_RULES) || ((victim.isRaid()) && Config.DEEPBLUE_DROP_RULES_RAID))
+ {
+ int levelDifference = victim.getLevel() - killer.getLevel();
+ if (item.getItemId() == Inventory.ADENA_ID)
+ {
+
+ return Util.map(levelDifference, -Config.DROP_ADENA_MAX_LEVEL_DIFFERENCE, -Config.DROP_ADENA_MIN_LEVEL_DIFFERENCE, Config.DROP_ADENA_MIN_LEVEL_GAP_CHANCE, 100.0) / 100;
+ }
+ return Util.map(levelDifference, -Config.DROP_ITEM_MAX_LEVEL_DIFFERENCE, -Config.DROP_ITEM_MIN_LEVEL_DIFFERENCE, Config.DROP_ITEM_MIN_LEVEL_GAP_CHANCE, 100.0) / 100;
+ }
+ return 1;
+ };
+
+ IKillerChanceModifierStrategy NO_RULES = (item, victim, killer) -> 1;
+
+ public double getKillerChanceModifier(IDropItem item, L2Character victim, L2Character killer);
+
+ @Override
+ public default double getKillerChanceModifier(GeneralDropItem item, L2Character victim, L2Character killer)
+ {
+ return getKillerChanceModifier((IDropItem) item, victim, killer);
+ }
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/INonGroupedKillerChanceModifierStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/INonGroupedKillerChanceModifierStrategy.java
new file mode 100644
index 0000000..a84fd5a
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/INonGroupedKillerChanceModifierStrategy.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+
+/**
+ * @author Battlecruiser
+ */
+public interface INonGroupedKillerChanceModifierStrategy
+{
+ public double getKillerChanceModifier(GeneralDropItem item, L2Character victim, L2Character killer);
+}
diff --git a/java/com/l2jserver/gameserver/model/drops/strategy/IPreciseDeterminationStrategy.java b/java/com/l2jserver/gameserver/model/drops/strategy/IPreciseDeterminationStrategy.java
new file mode 100644
index 0000000..57599c9
--- /dev/null
+++ b/java/com/l2jserver/gameserver/model/drops/strategy/IPreciseDeterminationStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2004-2014 L2J Server
+ *
+ * This file is part of L2J Server.
+ *
+ * L2J Server is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * L2J Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.drops.strategy;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.model.drops.IDropItem;
+
+/**
+ * @author Battlecruiser
+ */
+public interface IPreciseDeterminationStrategy
+{
+ public static final IPreciseDeterminationStrategy ALWAYS = dropItem -> true;
+
+ public static final IPreciseDeterminationStrategy DEFAULT = dropItem -> Config.PRECISE_DROP_CALCULATION;
+
+ public static final IPreciseDeterminationStrategy NEVER = dropItem -> false;
+
+ /**
+ * @param dropItem
+ * @return <code>true</code> if drop calculation strategy should use precise rules
+ */
+ public boolean isPreciseCalculated(IDropItem dropItem);
+}
diff --git a/java/com/l2jserver/gameserver/model/event/LongTimeEvent.java b/java/com/l2jserver/gameserver/model/event/LongTimeEvent.java
index 4ae519c..1244d30 100644
--- a/java/com/l2jserver/gameserver/model/event/LongTimeEvent.java
+++ b/java/com/l2jserver/gameserver/model/event/LongTimeEvent.java
@@ -1,18 +1,18 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -38,6 +38,7 @@ import com.l2jserver.gameserver.datatables.EventDroplist;
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.datatables.NpcData;
import com.l2jserver.gameserver.model.Location;
+import com.l2jserver.gameserver.model.drops.DropListScope;
import com.l2jserver.gameserver.model.drops.GeneralDropItem;
import com.l2jserver.gameserver.model.quest.Quest;
import com.l2jserver.gameserver.script.DateRange;
@@ -50,38 +51,38 @@ import com.l2jserver.gameserver.script.DateRange;
public class LongTimeEvent extends Quest
{
private String _eventName;
-
+
// Messages
private String _onEnterMsg = "Event is in process";
protected String _endMsg = "Event ends!";
-
+
private DateRange _eventPeriod = null;
private DateRange _dropPeriod;
-
+
// NPC's to spawm and their spawn points
private final List<NpcSpawn> _spawnList = new ArrayList<>();
-
+
// Drop data for event
private final List<GeneralDropItem> _dropList = new ArrayList<>();
-
+
private class NpcSpawn
{
protected final Location loc;
protected final int npcId;
-
+
protected NpcSpawn(int pNpcId, Location spawnLoc)
{
loc = spawnLoc;
npcId = pNpcId;
}
}
-
+
public LongTimeEvent(String name, String descr)
{
super(-1, name, descr);
-
+
loadConfig();
-
+
if (_eventPeriod != null)
{
if (_eventPeriod.isWithinRange(new Date()))
@@ -101,7 +102,7 @@ public class LongTimeEvent extends Quest
}
}
}
-
+
/**
* Load event configuration file
*/
@@ -120,7 +121,7 @@ public class LongTimeEvent extends Quest
_eventName = doc.getDocumentElement().getAttributes().getNamedItem("name").getNodeValue();
String period = doc.getDocumentElement().getAttributes().getNamedItem("active").getNodeValue();
_eventPeriod = DateRange.parse(period, new SimpleDateFormat("dd MM yyyy", Locale.US));
-
+
if (doc.getDocumentElement().getAttributes().getNamedItem("dropPeriod") != null)
{
String dropPeriod = doc.getDocumentElement().getAttributes().getNamedItem("dropPeriod").getNodeValue();
@@ -135,14 +136,14 @@ public class LongTimeEvent extends Quest
{
_dropPeriod = _eventPeriod; // Drop period, if not specified, assumes all event period.
}
-
+
if (_eventPeriod == null)
{
throw new NullPointerException("WARNING!!! " + getScriptName() + " event: illegal event period");
}
-
+
Date today = new Date();
-
+
if (_eventPeriod.getStartDate().after(today) || _eventPeriod.isWithinRange(today))
{
Node first = doc.getDocumentElement().getFirstChild();
@@ -162,31 +163,31 @@ public class LongTimeEvent extends Quest
int maxCount = Integer.parseInt(d.getAttributes().getNamedItem("max").getNodeValue());
String chance = d.getAttributes().getNamedItem("chance").getNodeValue();
int finalChance = 0;
-
+
if (!chance.isEmpty() && chance.endsWith("%"))
{
finalChance = Integer.parseInt(chance.substring(0, chance.length() - 1)) * 10000;
}
-
+
if (ItemTable.getInstance().getTemplate(itemId) == null)
{
_log.warning(getScriptName() + " event: " + itemId + " is wrong item id, item was not added in droplist");
continue;
}
-
+
if (minCount > maxCount)
{
_log.warning(getScriptName() + " event: item " + itemId + " - min greater than max, item was not added in droplist");
continue;
}
-
+
if ((finalChance < 10000) || (finalChance > 1000000))
{
_log.warning(getScriptName() + " event: item " + itemId + " - incorrect drop chance, item was not added in droplist");
continue;
}
-
- _dropList.add(new GeneralDropItem(itemId, minCount, maxCount, finalChance));
+
+ _dropList.add((GeneralDropItem) DropListScope.STATIC.newDropItem(itemId, minCount, maxCount, finalChance));
}
catch (NumberFormatException nfe)
{
@@ -209,13 +210,13 @@ public class LongTimeEvent extends Quest
int yPos = Integer.parseInt(d.getAttributes().getNamedItem("y").getNodeValue());
int zPos = Integer.parseInt(d.getAttributes().getNamedItem("z").getNodeValue());
int heading = d.getAttributes().getNamedItem("heading").getNodeValue() != null ? Integer.parseInt(d.getAttributes().getNamedItem("heading").getNodeValue()) : 0;
-
+
if (NpcData.getInstance().getTemplate(npcId) == null)
{
_log.warning(getScriptName() + " event: " + npcId + " is wrong NPC id, NPC was not added in spawnlist");
continue;
}
-
+
_spawnList.add(new NpcSpawn(npcId, new Location(xPos, yPos, zPos, heading)));
}
catch (NumberFormatException nfe)
@@ -256,7 +257,7 @@ public class LongTimeEvent extends Quest
_log.log(Level.WARNING, getScriptName() + " event: error reading " + configFile.getAbsolutePath() + " ! " + e.getMessage(), e);
}
}
-
+
/**
* Maintenance event start - adds global drop, spawns event NPC's, shows start announcement.
*/
@@ -270,7 +271,7 @@ public class LongTimeEvent extends Quest
EventDroplist.getInstance().addGlobalDrop(drop.getItemId(), drop.getMin(), drop.getMax(), (int) drop.getChance(), _dropPeriod);
}
}
-
+
// Add spawns
Long millisToEventEnd = _eventPeriod.getEndDate().getTime() - System.currentTimeMillis();
if (_spawnList != null)
@@ -280,17 +281,17 @@ public class LongTimeEvent extends Quest
addSpawn(spawn.npcId, spawn.loc.getX(), spawn.loc.getY(), spawn.loc.getZ(), spawn.loc.getHeading(), false, millisToEventEnd, false);
}
}
-
+
// Send message on begin
Announcements.getInstance().announceToAll(_onEnterMsg);
-
+
// Add announce for entering players
Announcements.getInstance().addEventAnnouncement(_eventPeriod, _onEnterMsg);
-
+
// Schedule event end (now only for message sending)
ThreadPoolManager.getInstance().scheduleGeneral(new ScheduleEnd(), millisToEventEnd);
}
-
+
/**
* @return event period
*/
@@ -298,7 +299,7 @@ public class LongTimeEvent extends Quest
{
return _eventPeriod;
}
-
+
/**
* @return {@code true} if now is event period
*/
@@ -306,7 +307,7 @@ public class LongTimeEvent extends Quest
{
return _eventPeriod.isWithinRange(new Date());
}
-
+
/**
* @return {@code true} if now is drop period
*/
@@ -314,7 +315,7 @@ public class LongTimeEvent extends Quest
{
return _dropPeriod.isWithinRange(new Date());
}
-
+
protected class ScheduleStart implements Runnable
{
@Override
@@ -323,7 +324,7 @@ public class LongTimeEvent extends Quest
startEvent();
}
}
-
+
protected class ScheduleEnd implements Runnable
{
@Override
diff --git a/java/com/l2jserver/gameserver/model/events/AbstractScript.java b/java/com/l2jserver/gameserver/model/events/AbstractScript.java
index aebe92e..8191a1d 100644
--- a/java/com/l2jserver/gameserver/model/events/AbstractScript.java
+++ b/java/com/l2jserver/gameserver/model/events/AbstractScript.java
@@ -24,8 +24,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -57,6 +61,9 @@ import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2TrapInstance;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
+import com.l2jserver.gameserver.model.drops.GeneralDropItem;
+import com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem;
+import com.l2jserver.gameserver.model.drops.IDropItem;
import com.l2jserver.gameserver.model.entity.Castle;
import com.l2jserver.gameserver.model.entity.Fort;
import com.l2jserver.gameserver.model.entity.Instance;
@@ -128,12 +135,14 @@ import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.scripting.ManagedScript;
import com.l2jserver.gameserver.util.MinionList;
import com.l2jserver.util.Rnd;
+import com.l2jserver.util.Util;
/**
* @author UnAfraid
*/
public abstract class AbstractScript extends ManagedScript
{
+
protected static final Logger _log = Logger.getLogger(AbstractScript.class.getName());
private final Map<ListenerRegisterType, Set<Integer>> _registeredIds = new ConcurrentHashMap<>();
private final List<AbstractEventListener> _listeners = new FastList<AbstractEventListener>().shared();
@@ -2193,6 +2202,496 @@ public abstract class AbstractScript extends ManagedScript
}
/**
+ * Gives an item to the player
+ * @param player
+ * @param item
+ * @param victim the character that "dropped" the item
+ * @return <code>true</code> if at least one item was given, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim)
+ {
+ List<ItemHolder> items = item.calculateDrops(victim, player);
+ if ((items == null) || items.isEmpty())
+ {
+ return false;
+ }
+ giveItems(player, items);
+ return true;
+ }
+
+ /**
+ * Gives an item to the player
+ * @param player
+ * @param items
+ */
+ protected static void giveItems(L2PcInstance player, List<ItemHolder> items)
+ {
+ for (ItemHolder item : items)
+ {
+ giveItems(player, item);
+ }
+
+ }
+
+ /**
+ * Gives an item to the player
+ * @param player
+ * @param item
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached.
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, ItemHolder item, long limit)
+ {
+ long maxToGive = limit - player.getInventory().getInventoryItemCount(item.getId(), -1);
+ if (maxToGive <= 0)
+ {
+ return false;
+ }
+ giveItems(player, item.getId(), Math.min(maxToGive, item.getCount()));
+ return true;
+ }
+
+ protected static boolean giveItems(L2PcInstance player, ItemHolder item, long limit, boolean playSound)
+ {
+ boolean drop = giveItems(player, item, limit);
+ if (drop && playSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ return drop;
+ }
+
+ /**
+ * @param player
+ * @param items
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached.
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, long limit)
+ {
+ boolean b = false;
+ for (ItemHolder item : items)
+ {
+ b |= giveItems(player, item, limit);
+ }
+ return b;
+ }
+
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, long limit, boolean playSound)
+ {
+ boolean drop = giveItems(player, items, limit);
+ if (drop && playSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ return drop;
+ }
+
+ /**
+ * @param player
+ * @param items
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached. If a no limit for an itemId is specified, item will always be given
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, Map<Integer, Long> limit)
+ {
+ return giveItems(player, items, Util.mapToFunction(limit));
+ }
+
+ /**
+ * @param player
+ * @param items
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached. If a no limit for an itemId is specified, item will always be given
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, Function<Integer, Long> limit)
+ {
+ boolean b = false;
+ for (ItemHolder item : items)
+ {
+ if (limit != null)
+ {
+ Long longLimit = limit.apply(item.getId());
+ // null -> no limit specified for that item id. This trick is to avoid limit.apply() be called twice (once for the null check)
+ if (longLimit != null)
+ {
+ b |= giveItems(player, item, longLimit);
+ continue; // the item is given, continue with next
+ }
+ }
+ // da BIG else
+ // no limit specified here (either limit or limit.apply(item.getId()) is null)
+ b = true;
+ giveItems(player, item);
+
+ }
+ return b;
+ }
+
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, Function<Integer, Long> limit, boolean playSound)
+ {
+ boolean drop = giveItems(player, items, limit);
+ if (drop && playSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ return drop;
+ }
+
+ protected static boolean giveItems(L2PcInstance player, List<ItemHolder> items, Map<Integer, Long> limit, boolean playSound)
+ {
+ return giveItems(player, items, Util.mapToFunction(limit), playSound);
+ }
+
+ /**
+ * @param player
+ * @param item
+ * @param victim the character that "dropped" the item
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached.
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, int limit)
+ {
+ return giveItems(player, item.calculateDrops(victim, player), limit);
+ }
+
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, int limit, boolean playSound)
+ {
+ boolean drop = giveItems(player, item, victim, limit);
+ if (drop && playSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ return drop;
+ }
+
+ /**
+ * @param player
+ * @param item
+ * @param victim the character that "dropped" the item
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached. If a no limit for an itemId is specified, item will always be given
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, Map<Integer, Long> limit)
+ {
+ return giveItems(player, item.calculateDrops(victim, player), limit);
+ }
+
+ /**
+ * @param player
+ * @param item
+ * @param victim the character that "dropped" the item
+ * @param limit the maximum amount of items the player can have. Won't give more if this limit is reached. If a no limit for an itemId is specified, item will always be given
+ * @return <code>true</code> if at least one item was given to the player, <code>false</code> otherwise
+ */
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, Function<Integer, Long> limit)
+ {
+ return giveItems(player, item.calculateDrops(victim, player), limit);
+ }
+
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, Map<Integer, Long> limit, boolean playSound)
+ {
+ return giveItems(player, item, victim, Util.mapToFunction(limit), playSound);
+ }
+
+ protected static boolean giveItems(L2PcInstance player, IDropItem item, L2Character victim, Function<Integer, Long> limit, boolean playSound)
+ {
+ boolean drop = giveItems(player, item, victim, limit);
+ if (drop && playSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ return drop;
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, Collection<ItemHolder> items, Function<Integer, Long> limit, boolean playSound)
+ {
+ Map<L2PcInstance, Map<Integer, Long>> rewardedCounts = calculateDistribution(players, items, limit);
+ // now give the calculated items to the players
+ giveItems(rewardedCounts, playSound);
+ return rewardedCounts;
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, Collection<ItemHolder> items, Map<Integer, Long> limit, boolean playSound)
+ {
+ return distributeItems(players, items, Util.mapToFunction(limit), playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, Collection<ItemHolder> items, long limit, boolean playSound)
+ {
+ return distributeItems(players, items, t -> limit, playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param item the items to distribute
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Long> distributeItems(Collection<L2PcInstance> players, ItemHolder item, long limit, boolean playSound)
+ {
+ Map<L2PcInstance, Map<Integer, Long>> distribution = distributeItems(players, Collections.singletonList(item), limit, playSound);
+ Map<L2PcInstance, Long> returnMap = new HashMap<>();
+ for (Entry<L2PcInstance, Map<Integer, Long>> entry : distribution.entrySet())
+ {
+ for (Entry<Integer, Long> entry2 : entry.getValue().entrySet())
+ {
+ returnMap.put(entry.getKey(), entry2.getValue());
+ }
+ }
+ return returnMap;
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, IDropItem items, L2Character killer, L2Character victim, Function<Integer, Long> limit, boolean playSound)
+ {
+ return distributeItems(players, items.calculateDrops(victim, killer), limit, playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, IDropItem items, L2Character killer, L2Character victim, Map<Integer, Long> limit, boolean playSound)
+ {
+ return distributeItems(players, items.calculateDrops(victim, killer), limit, playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, IDropItem items, L2Character killer, L2Character victim, long limit, boolean playSound)
+ {
+ return distributeItems(players, items.calculateDrops(victim, killer), limit, playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @param smartDrop true if to not calculate a drop, which can't be given to any player 'cause of limits
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, final GroupedGeneralDropItem items, L2Character killer, L2Character victim, Function<Integer, Long> limit, boolean playSound, boolean smartDrop)
+ {
+ GroupedGeneralDropItem toDrop;
+ if (smartDrop)
+ {
+ toDrop = new GroupedGeneralDropItem(items.getChance(), items.getDropCalculationStrategy(), items.getKillerChanceModifierStrategy(), items.getPreciseStrategy());
+ List<GeneralDropItem> dropItems = new LinkedList<>(items.getItems());
+ itemLoop: for (Iterator<GeneralDropItem> it = dropItems.iterator(); it.hasNext();)
+ {
+ GeneralDropItem item = it.next();
+ for (L2PcInstance player : players)
+ {
+ int itemId = item.getItemId();
+ if (player.getInventory().getInventoryItemCount(itemId, -1, true) < avoidNull(limit, itemId))
+ {
+ // we can give this item to this player
+ continue itemLoop;
+ }
+ }
+ // there's nobody to give this item to
+ it.remove();
+ }
+ toDrop.setItems(dropItems);
+ toDrop = toDrop.normalizeMe(victim, killer);
+ }
+ else
+ {
+ toDrop = items;
+ }
+ return distributeItems(players, toDrop, killer, victim, limit, playSound);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @param smartDrop true if to not calculate a drop, which can't be given to any player
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, final GroupedGeneralDropItem items, L2Character killer, L2Character victim, Map<Integer, Long> limit, boolean playSound, boolean smartDrop)
+ {
+ return distributeItems(players, items, killer, victim, Util.mapToFunction(limit), playSound, smartDrop);
+ }
+
+ /**
+ * Distributes items to players equally
+ * @param players the players to whom the items will be distributed
+ * @param items the items to distribute
+ * @param killer the one who "kills" the victim
+ * @param victim the character that "dropped" the item
+ * @param limit the limit what single player can have of each item
+ * @param playSound if to play sound if a player gets at least one item
+ * @param smartDrop true if to not calculate a drop, which can't be given to any player
+ * @return the counts of each items given to each player
+ */
+ protected static Map<L2PcInstance, Map<Integer, Long>> distributeItems(Collection<L2PcInstance> players, final GroupedGeneralDropItem items, L2Character killer, L2Character victim, long limit, boolean playSound, boolean smartDrop)
+ {
+ return distributeItems(players, items, killer, victim, t -> limit, playSound, smartDrop);
+ }
+
+ /**
+ * @param players
+ * @param items
+ * @param limit
+ * @return
+ */
+ private static Map<L2PcInstance, Map<Integer, Long>> calculateDistribution(Collection<L2PcInstance> players, Collection<ItemHolder> items, Function<Integer, Long> limit)
+ {
+ Map<L2PcInstance, Map<Integer, Long>> rewardedCounts = new HashMap<>();
+ for (L2PcInstance player : players)
+ {
+ rewardedCounts.put(player, new HashMap<Integer, Long>());
+ }
+ for (ItemHolder item : items)
+ {
+ long equaldist = item.getCount() / players.size();
+ long randomdist = item.getCount() % players.size();
+ List<L2PcInstance> toDist = new ArrayList<>(players);
+ do // this must happen at least once in order to get away already full players (and then equaldist can become nonzero)
+ {
+ for (Iterator<L2PcInstance> it = toDist.iterator(); it.hasNext();)
+ {
+ L2PcInstance player = it.next();
+ if (!rewardedCounts.get(player).containsKey(item.getId()))
+ {
+ rewardedCounts.get(player).put(item.getId(), 0L);
+ }
+ long maxGive = avoidNull(limit, item.getId()) - player.getInventory().getInventoryItemCount(item.getId(), -1, true) - rewardedCounts.get(player).get(item.getId());
+ long toGive = equaldist;
+ if (equaldist >= maxGive)
+ {
+ toGive = maxGive;
+ randomdist += (equaldist - maxGive); // overflown items are available to next players
+ it.remove(); // this player is already full
+ }
+ rewardedCounts.get(player).put(item.getId(), rewardedCounts.get(player).get(item.getId()) + toGive);
+ }
+ equaldist = randomdist / toDist.size(); // the rest of items may be allowed to be equally distributed between remaining players
+ randomdist %= toDist.size();
+ }
+ while (equaldist > 0);
+ while (randomdist > 0)
+ {
+ if (toDist.isEmpty())
+ {
+ // we don't have any player left
+ break;
+ }
+ L2PcInstance player = toDist.get(getRandom(toDist.size()));
+ // avoid null return
+ long maxGive = avoidNull(limit, item.getId()) - limit.apply(item.getId()) - player.getInventory().getInventoryItemCount(item.getId(), -1, true) - rewardedCounts.get(player).get(item.getId());
+ if (maxGive > 0)
+ {
+ // we can add an item to player
+ // so we add one item to player
+ rewardedCounts.get(player).put(item.getId(), rewardedCounts.get(player).get(item.getId()) + 1);
+ randomdist--;
+ }
+ toDist.remove(player); // Either way this player isn't allowable for next random award
+ }
+ }
+ return rewardedCounts;
+ }
+
+ /**
+ * This function is for avoidance null returns in function limits
+ * @param <T> the type of function arg
+ * @param function the function
+ * @param arg the argument
+ * @return {@link Long#MAX_VALUE} if function.apply(arg) is null, function.apply(arg) otherwise
+ */
+ private static <T> long avoidNull(Function<T, Long> function, T arg)
+ {
+ Long longLimit = function.apply(arg);
+ return longLimit == null ? Long.MAX_VALUE : longLimit;
+ }
+
+ /**
+ * Distributes items to players
+ * @param rewardedCounts A scheme of distribution items (the structure is: Map<player Map<itemId, count>>)
+ * @param playSound if to play sound if a player gets at least one item
+ */
+ private static void giveItems(Map<L2PcInstance, Map<Integer, Long>> rewardedCounts, boolean playSound)
+ {
+ for (Entry<L2PcInstance, Map<Integer, Long>> entry : rewardedCounts.entrySet())
+ {
+ L2PcInstance player = entry.getKey();
+ boolean playPlayerSound = false;
+ for (Entry<Integer, Long> item : entry.getValue().entrySet())
+ {
+ if (item.getValue() >= 0)
+ {
+ playPlayerSound = true;
+ giveItems(player, item.getKey(), item.getValue());
+ }
+ }
+ if (playSound && playPlayerSound)
+ {
+ playSound(player, QuestSound.ITEMSOUND_QUEST_ITEMGET);
+ }
+ }
+ }
+
+ /**
* Take an amount of a specified item from player's inventory.
* @param player the player whose item to take
* @param itemId the ID of the item to take
diff --git a/java/com/l2jserver/gameserver/model/quest/Quest.java b/java/com/l2jserver/gameserver/model/quest/Quest.java
index 284f3e4..8981721 100644
--- a/java/com/l2jserver/gameserver/model/quest/Quest.java
+++ b/java/com/l2jserver/gameserver/model/quest/Quest.java
@@ -1,18 +1,18 @@
/*
* Copyright (C) 2004-2014 L2J Server
- *
+ *
* This file is part of L2J Server.
- *
+ *
* L2J Server is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
- *
+ *
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
diff --git a/java/com/l2jserver/util/Util.java b/java/com/l2jserver/util/Util.java
index 5993703..f3fcd81 100644
--- a/java/com/l2jserver/util/Util.java
+++ b/java/com/l2jserver/util/Util.java
@@ -31,6 +31,8 @@ import java.time.temporal.TemporalAdjusters;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
import java.util.logging.Logger;
/**
@@ -223,4 +225,16 @@ public final class Util
.orElse(dateNowWithDifferentTime.with(TemporalAdjusters.next(daysOfWeek.get(0))));
// @formatter:on
}
+
+ /**
+ * This method translates map to function
+ * @param <K> key type of the map and argument of the function
+ * @param <V> value type of the map and return type of the function
+ * @param map the input map
+ * @return a function which returns map.get(arg)
+ */
+ public static <K, V> Function<K, V> mapToFunction(Map<K, V> map)
+ {
+ return key -> map.get(key);
+ }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment