package com.asteria.game.character.npc.drop; import static com.asteria.utility.Chance.RARE; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import com.asteria.game.GameConstants; import com.asteria.game.character.player.Player; import com.asteria.game.item.Item; import com.asteria.game.item.container.Equipment; import com.asteria.utility.ArrayIterator; import com.asteria.utility.Chance; import com.asteria.utility.RandomGen; /** * A container that holds the unique and common drop tables. * * @author lare96 <http://github.org/lare96> */ public final class NpcDropTable { /** * The unique drop table that consists of both dynamic and rare drops. */ private final NpcDrop[] unique; /** * The common drop table that is shared with other tables. */ private final NpcDropCache[] common; /** * Creates a new {@link NpcDropTable}. * * @param unique * the unique drop table. * @param common * the common drop table. */ public NpcDropTable(NpcDrop[] unique, NpcDropCache[] common) { this.unique = unique; this.common = common; } /** * Performs the necessary calculations on all of the tables in this * container to determine an array of items to drop. Please note that this * is not a static implementation meaning that calling this multiple times * will return a different array of items. * * @param player * the player that these calculations are being performed for. * @return the array of items that were calculated. */ public List<Item> toItems(Player player) { // Instantiate the random generator, the list of items to drop, the list // for the rare items, the common table, and a list that contains a // shuffled copy of the unique table. RandomGen random = new RandomGen(); List<Item> items = new LinkedList<>(); NpcDropCache cache = random.random(common); Iterator<NpcDrop> $it = new ArrayIterator<>(random.shuffle(unique.clone())); // Determines if the rare, common, and dynamic tables should be rolled. // The breakdown of each of the formulas are touched upon later on. boolean rollRare = random.get().nextInt(5) == 0; // 20% chance. boolean rollCommon = player != null && player.getEquipment().getId(Equipment.RING_SLOT) == 2572 ? random.get().nextInt(4) == 0 : random.get().nextInt(8) == 0; // 12.5%-25% chance. boolean rollDynamic = random.get().nextBoolean(); // 50% chance. // Iterate through the unique table, drop ALWAYS items, roll a RARE+ // item if possible, and roll dynamic items if possible. int amount = 0; while ($it.hasNext()) { NpcDrop next = $it.next(); Chance chance = next.getChance(); if (chance.getTier() == 0) { // 100% Chance to drop an item from the always table, the lowest // Chance tier. items.add(next.toItem(random)); } else if (chance.getTier() >= RARE.getTier() && rollRare) { // 20% Chance to roll an item from the rare table, pick one drop // from the table and roll it. if (chance.successful(random)) items.add(next.toItem(random)); rollRare = false; } else if (rollDynamic && chance.getTier() < RARE.getTier()) { // 50% Chance to roll an item from the dynamic table, pick one // drop from the table and roll it. if (amount++ == GameConstants.DROP_THRESHOLD) rollDynamic = false; if (next.getChance().successful(random)) items.add(next.toItem(random)); } if (!$it.hasNext() && rollCommon) { // n (n = 12.5% chance, 25% if wearing Ring of Wealth) // Chance to roll an item from the common table, pick one drop // from the table and roll it. next = random.random(NpcDropManager.COMMON.get(cache)); if (next.getChance().successful(random)) items.add(next.toItem(random)); rollCommon = false; } } return items; } }