IGroupedItemDropCalculationStrategy.java 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. * Copyright © 2004-2021 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.model.drops.strategy;
  20. import com.l2jserver.commons.util.Rnd;
  21. import com.l2jserver.gameserver.model.actor.L2Character;
  22. import com.l2jserver.gameserver.model.drops.GeneralDropItem;
  23. import com.l2jserver.gameserver.model.drops.GroupedGeneralDropItem;
  24. import com.l2jserver.gameserver.model.drops.IDropItem;
  25. import com.l2jserver.gameserver.model.holders.ItemHolder;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import java.util.ArrayList;
  29. import java.util.Collections;
  30. import java.util.HashMap;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.concurrent.ConcurrentHashMap;
  34. import static com.l2jserver.gameserver.config.Configuration.general;
  35. /**
  36. * @author Battlecruiser
  37. */
  38. public interface IGroupedItemDropCalculationStrategy {
  39. Logger LOG = LoggerFactory.getLogger(IGroupedItemDropCalculationStrategy.class);
  40. /**
  41. * 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.
  42. */
  43. IGroupedItemDropCalculationStrategy DEFAULT_STRATEGY = new IGroupedItemDropCalculationStrategy() {
  44. private final Map<GroupedGeneralDropItem, GeneralDropItem> singleItemCache = new ConcurrentHashMap<>();
  45. private GeneralDropItem getSingleItem(GroupedGeneralDropItem dropItem) {
  46. final GeneralDropItem item1 = dropItem.getItems().iterator().next();
  47. singleItemCache.putIfAbsent(dropItem, new GeneralDropItem(item1.getItemId(), item1.getMin(), item1.getMax(), (item1.getChance() * dropItem.getChance()) / 100, //
  48. item1.getAmountStrategy(), item1.getChanceStrategy(), dropItem.getPreciseStrategy(), dropItem.getKillerChanceModifierStrategy(), item1.getDropCalculationStrategy()));
  49. return singleItemCache.get(dropItem);
  50. }
  51. @Override
  52. public List<ItemHolder> calculateDrops(GroupedGeneralDropItem dropItem, L2Character victim, L2Character killer) {
  53. if (dropItem.getItems().size() == 1) {
  54. return getSingleItem(dropItem).calculateDrops(victim, killer);
  55. }
  56. GroupedGeneralDropItem normalized = dropItem.normalizeMe(victim, killer);
  57. if (normalized.getChance() > (Rnd.nextDouble() * 100)) {
  58. final double random = (Rnd.nextDouble() * 100);
  59. double totalChance = 0;
  60. for (GeneralDropItem item2 : normalized.getItems()) {
  61. // Grouped item chance rates should not be modified (the whole magic was already done by normalizing thus the items' chance sum is always 100%).
  62. totalChance += item2.getChance();
  63. if (totalChance > random) {
  64. int amountMultiply = 1;
  65. if (dropItem.isPreciseCalculated() && (normalized.getChance() >= 100)) {
  66. amountMultiply = (int) (normalized.getChance()) / 100;
  67. if ((normalized.getChance() % 100) > (Rnd.nextDouble() * 100)) {
  68. amountMultiply++;
  69. }
  70. }
  71. return Collections.singletonList(new ItemHolder(item2.getItemId(), Rnd.get(item2.getMin(victim), item2.getMax(victim)) * amountMultiply));
  72. }
  73. }
  74. }
  75. return null;
  76. }
  77. };
  78. /**
  79. * This strategy calculates a group's drop by calculating drops of its individual items and merging its results.
  80. */
  81. IGroupedItemDropCalculationStrategy DISBAND_GROUP = (item, victim, killer) -> {
  82. List<ItemHolder> dropped = new ArrayList<>();
  83. for (IDropItem dropItem : item.extractMe()) {
  84. dropped.addAll(dropItem.calculateDrops(victim, killer));
  85. }
  86. return dropped.isEmpty() ? null : dropped;
  87. };
  88. /**
  89. * 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.
  90. */
  91. IGroupedItemDropCalculationStrategy PRECISE_MULTIPLE_GROUP_ROLLS = (item, victim, killer) -> {
  92. if (!item.isPreciseCalculated()) {
  93. // if item hasn't precise calculation there's no change from DEFAULT_STRATEGY
  94. return DEFAULT_STRATEGY.calculateDrops(item, victim, victim);
  95. }
  96. GroupedGeneralDropItem newItem = new GroupedGeneralDropItem(item.getChance(), DEFAULT_STRATEGY, item.getKillerChanceModifierStrategy(), IPreciseDeterminationStrategy.NEVER);
  97. newItem.setItems(item.getItems());
  98. GroupedGeneralDropItem normalized = newItem.normalizeMe(victim, killer);
  99. // Let's determine the number of rolls.
  100. int rolls = (int) (normalized.getChance() / 100);
  101. if ((Rnd.nextDouble() * 100) < (normalized.getChance() % 100)) {
  102. rolls++;
  103. }
  104. List<ItemHolder> dropped = new ArrayList<>(rolls);
  105. for (int i = 0; i < rolls; i++) {
  106. // 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.
  107. List<ItemHolder> drops = normalized.calculateDrops(victim, killer);
  108. if (drops != null) {
  109. dropped.addAll(drops);
  110. }
  111. }
  112. if (general().preciseDropMultipleRollsAggregateDrops()) {
  113. Map<Integer, Long> countByItemId = new HashMap<>();
  114. for (ItemHolder drop : dropped) {
  115. Long currentCount = countByItemId.getOrDefault(drop.getId(), 0L);
  116. countByItemId.put(drop.getId(), currentCount + drop.getCount());
  117. }
  118. dropped.clear();
  119. for (Map.Entry<Integer, Long> entry : countByItemId.entrySet()) {
  120. dropped.add(new ItemHolder(entry.getKey(), entry.getValue()));
  121. }
  122. }
  123. return dropped.isEmpty() ? null : dropped;
  124. };
  125. List<ItemHolder> calculateDrops(GroupedGeneralDropItem item, L2Character victim, L2Character killer);
  126. }