瀏覽代碼

Alchemy cleanup, part 1.

GJ 11 年之前
父節點
當前提交
988006f913

+ 2 - 2
src/main/java/com/gmail/nossr50/commands/skills/AlchemyCommand.java

@@ -33,8 +33,8 @@ public class AlchemyCommand extends SkillCommand {
         AlchemyManager alchemyManager = UserManager.getPlayer(player).getAlchemyManager();
         String[] displayValues = new String[2];
 
-        displayValues[0] = decimal.format(alchemyManager.getBrewSpeed()) + "x";
-        displayValues[1] = isLucky ? decimal.format(alchemyManager.getBrewSpeedLucky()) + "x" : null;
+        displayValues[0] = decimal.format(alchemyManager.calculateBrewSpeed(false)) + "x";
+        displayValues[1] = isLucky ? decimal.format(alchemyManager.calculateBrewSpeed(true)) + "x" : null;
 
         return displayValues;
     }

+ 40 - 9
src/main/java/com/gmail/nossr50/config/skills/alchemy/PotionConfig.java

@@ -5,6 +5,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.gmail.nossr50.skills.alchemy.Alchemy;
 import org.bukkit.Material;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.inventory.ItemStack;
@@ -18,16 +19,16 @@ import com.gmail.nossr50.datatypes.skills.alchemy.AlchemyPotion;
 public class PotionConfig extends ConfigLoader {
     private static PotionConfig instance;
 
-    public List<ItemStack> concoctionsIngredientsTierOne = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierTwo = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierThree = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierFour = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierFive = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierSix = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierSeven = new ArrayList<ItemStack>();
-    public List<ItemStack> concoctionsIngredientsTierEight = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierOne = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierTwo = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierThree = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierFour = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierFive = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierSix = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierSeven = new ArrayList<ItemStack>();
+    private List<ItemStack> concoctionsIngredientsTierEight = new ArrayList<ItemStack>();
 
-    public Map<Short, AlchemyPotion> potionMap = new HashMap<Short, AlchemyPotion>();
+    private Map<Short, AlchemyPotion> potionMap = new HashMap<Short, AlchemyPotion>();
 
     private PotionConfig() {
         super("potions.yml");
@@ -189,4 +190,34 @@ public class PotionConfig extends ConfigLoader {
 
         return null;
     }
+
+    public List<ItemStack> getIngredients(int tier) {
+        switch (tier) {
+            case 8:
+                return concoctionsIngredientsTierEight;
+            case 7:
+                return concoctionsIngredientsTierSeven;
+            case 6:
+                return concoctionsIngredientsTierSix;
+            case 5:
+                return concoctionsIngredientsTierFive;
+            case 4:
+                return concoctionsIngredientsTierFour;
+            case 3:
+                return concoctionsIngredientsTierThree;
+            case 2:
+                return concoctionsIngredientsTierTwo;
+            case 1:
+            default:
+                return concoctionsIngredientsTierOne;
+        }
+    }
+
+    public boolean isValidPotion(ItemStack item) {
+        return potionMap.containsKey(item.getDurability());
+    }
+
+    public AlchemyPotion getPotion(short durability) {
+        return potionMap.get(durability);
+    }
 }

+ 0 - 4
src/main/java/com/gmail/nossr50/datatypes/skills/alchemy/AlchemyPotion.java

@@ -28,10 +28,6 @@ public class AlchemyPotion {
         return "AlchemyPotion{" + dataValue + "," + name + ",Effects[" + effects.size() + "], Children[" + children.size() + "]}";
     }
 
-    public ItemStack toItemStack() {
-        return toItemStack(1);
-    }
-
     public ItemStack toItemStack(int amount) {
         ItemStack potion = new ItemStack(Material.POTION, amount, this.getDataValue());
         PotionMeta meta = (PotionMeta) potion.getItemMeta();

+ 137 - 10
src/main/java/com/gmail/nossr50/listeners/InventoryListener.java

@@ -12,6 +12,7 @@ import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.ClickType;
 import org.bukkit.event.inventory.CraftItemEvent;
 import org.bukkit.event.inventory.FurnaceBurnEvent;
 import org.bukkit.event.inventory.FurnaceExtractEvent;
@@ -22,8 +23,10 @@ import org.bukkit.event.inventory.InventoryDragEvent;
 import org.bukkit.event.inventory.InventoryMoveItemEvent;
 import org.bukkit.event.inventory.InventoryOpenEvent;
 import org.bukkit.event.inventory.InventoryType;
+import org.bukkit.inventory.BrewerInventory;
 import org.bukkit.inventory.FurnaceInventory;
 import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.metadata.MetadataValue;
 
@@ -32,6 +35,7 @@ import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.datatypes.skills.SecondaryAbility;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.runnables.PlayerUpdateInventoryTask;
+import com.gmail.nossr50.skills.alchemy.Alchemy;
 import com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer;
 import com.gmail.nossr50.util.ItemUtils;
 import com.gmail.nossr50.util.Misc;
@@ -138,43 +142,166 @@ public class InventoryListener implements Listener {
 
     @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
     public void onInventoryClickEventNormal(InventoryClickEvent event) {
-        if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) {
+        Inventory inventory = event.getInventory();
+
+        if (!(inventory instanceof BrewerInventory)) {
+            return;
+        }
+
+        InventoryHolder holder = inventory.getHolder();
+
+        if (!(holder instanceof BrewingStand)) {
+            return;
+        }
+
+        HumanEntity whoClicked = event.getWhoClicked();
+
+        if (Misc.isNPCEntity(whoClicked) || !(whoClicked instanceof Player) || !Permissions.secondaryAbilityEnabled(whoClicked, SecondaryAbility.CONCOCTIONS)) {
             return;
         }
 
-        if (!(event.getWhoClicked() instanceof Player) || Misc.isNPCEntity(event.getWhoClicked()) || !Permissions.concoctions(event.getWhoClicked())) {
+        Player player = (Player) whoClicked;
+        BrewingStand stand = (BrewingStand) holder;
+        ItemStack clicked = event.getCurrentItem();
+
+        if (clicked != null && clicked.getType() == Material.POTION) {
+            AlchemyPotionBrewer.scheduleCheck(player, stand);
             return;
         }
 
-        AlchemyPotionBrewer.handleInventoryClick(event);
+        ClickType click = event.getClick();
+        InventoryType.SlotType slot = event.getSlotType();
+
+        if (click.isShiftClick()) {
+            switch (slot) {
+                case FUEL:
+                    AlchemyPotionBrewer.scheduleCheck(player, stand);
+                    return;
+                case CONTAINER:
+                case QUICKBAR:
+                    if (!AlchemyPotionBrewer.isValidIngredient(player, clicked)) {
+                        return;
+                    }
+
+                    AlchemyPotionBrewer.transferItems(event.getView(), event.getRawSlot(), Alchemy.INGREDIENT_SLOT, click);
+                    event.setCancelled(true);
+                    AlchemyPotionBrewer.scheduleUpdate(inventory);
+                    AlchemyPotionBrewer.scheduleCheck(player, stand);
+                    return;
+                default:
+                    return;
+            }
+        }
+        else if (slot == InventoryType.SlotType.FUEL) {
+            ItemStack cursor = event.getCursor();
+            boolean emptyClicked = AlchemyPotionBrewer.isEmpty(clicked);
+
+            if (AlchemyPotionBrewer.isEmpty(cursor)) {
+                if (emptyClicked) {
+                    if (click == ClickType.NUMBER_KEY) {
+                        AlchemyPotionBrewer.scheduleCheck(player, stand);
+                        return;
+                    }
+                }
+
+                AlchemyPotionBrewer.scheduleCheck(player, stand);
+            }
+            else if (emptyClicked) {
+                if (AlchemyPotionBrewer.isValidIngredient(player, cursor)) {
+                    int amount = cursor.getAmount();
+
+                    if (click == ClickType.LEFT || (click == ClickType.RIGHT && amount == 1)) {
+                        event.setCancelled(true);
+                        event.setCurrentItem(cursor.clone());
+                        event.setCursor(null);
+
+                        AlchemyPotionBrewer.scheduleUpdate(inventory);
+                        AlchemyPotionBrewer.scheduleCheck(player, stand);
+                    }
+                    else if (click == ClickType.RIGHT) {
+                        event.setCancelled(true);
+
+                        ItemStack one = cursor.clone();
+                        one.setAmount(1);
+
+                        ItemStack rest = cursor.clone();
+                        rest.setAmount(amount - 1);
+
+                        event.setCurrentItem(one);
+                        event.setCursor(rest);
+
+                        AlchemyPotionBrewer.scheduleUpdate(inventory);
+                        AlchemyPotionBrewer.scheduleCheck(player, stand);
+                    }
+                }
+            }
+        }
     }
 
     @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
     public void onInventoryDragEvent(InventoryDragEvent event) {
-        if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) {
+        Inventory inventory = event.getInventory();
+
+        if (!(inventory instanceof BrewerInventory)) {
+            return;
+        }
+
+        InventoryHolder holder = inventory.getHolder();
+
+        if (!(holder instanceof BrewingStand)) {
+            return;
+        }
+
+        HumanEntity whoClicked = event.getWhoClicked();
+
+        if (Misc.isNPCEntity(whoClicked) || !(whoClicked instanceof Player) || !Permissions.secondaryAbilityEnabled(whoClicked, SecondaryAbility.CONCOCTIONS)) {
             return;
         }
 
-        if (!(event.getWhoClicked() instanceof Player) || Misc.isNPCEntity(event.getWhoClicked()) || !Permissions.concoctions(event.getWhoClicked())) {
+        if (!event.getInventorySlots().contains(Alchemy.INGREDIENT_SLOT)) {
             return;
         }
 
-        AlchemyPotionBrewer.handleInventoryDrag(event);
+        ItemStack cursor = event.getCursor();
+        ItemStack ingredient = ((BrewerInventory) inventory).getIngredient();
+
+        if (AlchemyPotionBrewer.isEmpty(ingredient) || ingredient.isSimilar(cursor)) {
+            Player player = (Player) whoClicked;
+
+            if (AlchemyPotionBrewer.isValidIngredient(player, cursor)) {
+                // Not handled: dragging custom ingredients over ingredient slot (does not trigger any event)
+                AlchemyPotionBrewer.scheduleCheck(player, (BrewingStand) holder);
+                return;
+            }
+
+            event.setCancelled(true);
+            AlchemyPotionBrewer.scheduleUpdate(inventory);
+        }
     }
 
     @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
     public void onInventoryMoveItemEvent(InventoryMoveItemEvent event) {
-        if (event.getDestination().getType() != InventoryType.BREWING || !(event.getDestination().getHolder() instanceof BrewingStand)) {
+        Inventory inventory = event.getDestination();
+
+        if (!(inventory instanceof BrewerInventory)) {
+            return;
+        }
+
+        InventoryHolder holder = inventory.getHolder();
+
+        if (!(holder instanceof BrewingStand)) {
             return;
         }
 
-        if (Config.getInstance().getPreventHopperTransfer() && event.getItem() != null && event.getItem().getType() != Material.POTION) {
+        ItemStack item = event.getItem();
+
+        if (Config.getInstance().getPreventHopperTransfer() && item.getType() != Material.POTION) {
             event.setCancelled(true);
             return;
         }
 
-        if (Config.getInstance().getEnabledForHoppers()) {
-            AlchemyPotionBrewer.handleInventoryMoveItem(event);
+        if (Config.getInstance().getEnabledForHoppers() && AlchemyPotionBrewer.isValidIngredient(null, item)) {
+            AlchemyPotionBrewer.scheduleCheck(null, (BrewingStand) holder);
         }
     }
 

+ 1 - 4
src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewCheckTask.java

@@ -12,8 +12,6 @@ import com.gmail.nossr50.skills.alchemy.Alchemy;
 import com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer;
 
 public class AlchemyBrewCheckTask extends BukkitRunnable {
-    private final static int INGREDIENT_SLOT = 3;
-
     private Player player;
     private BrewingStand brewingStand;
     private ItemStack[] oldInventory;
@@ -30,7 +28,7 @@ public class AlchemyBrewCheckTask extends BukkitRunnable {
         ItemStack[] newInventory = Arrays.copyOfRange(((BrewingStand) block.getState()).getInventory().getContents(), 0, 4);
 
         if (Alchemy.brewingStandMap.containsKey(brewingStand)) {
-            if (oldInventory[INGREDIENT_SLOT] == null || newInventory[INGREDIENT_SLOT] == null || !oldInventory[INGREDIENT_SLOT].isSimilar(newInventory[INGREDIENT_SLOT]) || !AlchemyPotionBrewer.isValidBrew(player, newInventory)) {
+            if (oldInventory[Alchemy.INGREDIENT_SLOT] == null || newInventory[Alchemy.INGREDIENT_SLOT] == null || !oldInventory[Alchemy.INGREDIENT_SLOT].isSimilar(newInventory[Alchemy.INGREDIENT_SLOT]) || !AlchemyPotionBrewer.isValidBrew(player, newInventory)) {
                 Alchemy.brewingStandMap.get(brewingStand).cancelBrew();
             }
         }
@@ -39,5 +37,4 @@ public class AlchemyBrewCheckTask extends BukkitRunnable {
             Alchemy.brewingStandMap.put(brewingStand, new AlchemyBrewTask(brewingStand, player));
         }
     }
-
 }

+ 2 - 5
src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewTask.java

@@ -34,14 +34,11 @@ public class AlchemyBrewTask extends BukkitRunnable {
         brewTimer = DEFAULT_BREW_TICKS;
 
         if (player != null && !Misc.isNPCEntity(player) && Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CATALYSIS)) {
-            double catalysis = UserManager.getPlayer(player).getAlchemyManager().getBrewSpeed();
-
-            if (Permissions.lucky(player, SkillType.ALCHEMY)) {
-                catalysis = UserManager.getPlayer(player).getAlchemyManager().getBrewSpeedLucky();
-            }
+            double catalysis = UserManager.getPlayer(player).getAlchemyManager().calculateBrewSpeed(Permissions.lucky(player, SkillType.ALCHEMY));
 
             McMMOPlayerCatalysisEvent event = new McMMOPlayerCatalysisEvent(player, catalysis);
             mcMMO.p.getServer().getPluginManager().callEvent(event);
+
             if (!event.isCancelled()) {
                 brewSpeed = catalysis;
             }

+ 3 - 15
src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java

@@ -44,6 +44,8 @@ public final class Alchemy {
         }
     }
 
+    public static final int INGREDIENT_SLOT = 3;
+
     public static int    catalysisUnlockLevel   = AdvancedConfig.getInstance().getCatalysisUnlockLevel();
     public static int    catalysisMaxBonusLevel = AdvancedConfig.getInstance().getCatalysisMaxBonusLevel();
     public static double catalysisMinSpeed      = AdvancedConfig.getInstance().getCatalysisMinSpeed();
@@ -53,26 +55,12 @@ public final class Alchemy {
 
     private Alchemy() {}
 
-    /**
-     * Calculate base brewing speed, given a skill level and ignoring all perks.
-     *
-     * @param skillLevel Skill level used for calculation.
-     *
-     * @return Base brewing speed for the level.
-     */
-    public static double calculateBrewSpeed(int skillLevel) {
-        if (skillLevel < catalysisUnlockLevel) {
-            return catalysisMinSpeed;
-        }
-
-        return Math.min(catalysisMaxSpeed, catalysisMinSpeed + (catalysisMaxSpeed - catalysisMinSpeed) * (skillLevel - catalysisUnlockLevel) / (catalysisMaxBonusLevel - catalysisUnlockLevel));
-    }
-
     /**
      * Finish all active brews.  Used upon Disable to prevent vanilla potions from being brewed upon next Enable.
      */
     public static void finishAllBrews() {
         mcMMO.p.debug("Completing " + brewingStandMap.size() + " unfinished Alchemy brews.");
+
         for (AlchemyBrewTask alchemyBrewTask : brewingStandMap.values()) {
             alchemyBrewTask.finishImmediately();
         }

+ 15 - 44
src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java

@@ -2,15 +2,14 @@ package com.gmail.nossr50.skills.alchemy;
 
 import java.util.List;
 
+import com.gmail.nossr50.util.Permissions;
 import org.bukkit.inventory.ItemStack;
 
 import com.gmail.nossr50.config.experience.ExperienceConfig;
 import com.gmail.nossr50.config.skills.alchemy.PotionConfig;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
-import com.gmail.nossr50.datatypes.skills.SecondaryAbility;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.skills.SkillManager;
-import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.StringUtils;
 
 public class AlchemyManager extends SkillManager {
@@ -20,23 +19,6 @@ public class AlchemyManager extends SkillManager {
         super(mcMMOPlayer, SkillType.ALCHEMY);
     }
 
-    public boolean canCatalysis() {
-        return Permissions.secondaryAbilityEnabled(getPlayer(), SecondaryAbility.CATALYSIS);
-    }
-
-    public boolean canConcoctions() {
-        return Permissions.secondaryAbilityEnabled(getPlayer(), SecondaryAbility.CONCOCTIONS);
-    }
-
-    public boolean canUseIngredient(ItemStack item) {
-        for (ItemStack ingredient : getIngredients()) {
-            if (item.isSimilar(ingredient)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     public int getTier() {
         for (Alchemy.Tier tier : Alchemy.Tier.values()) {
             if (getSkillLevel() >= tier.getLevel()) {
@@ -48,31 +30,17 @@ public class AlchemyManager extends SkillManager {
     }
 
     public List<ItemStack> getIngredients() {
-        switch (Alchemy.Tier.fromNumerical(getTier())) {
-            case EIGHT:
-                return PotionConfig.getInstance().concoctionsIngredientsTierEight;
-            case SEVEN:
-                return PotionConfig.getInstance().concoctionsIngredientsTierSeven;
-            case SIX:
-                return PotionConfig.getInstance().concoctionsIngredientsTierSix;
-            case FIVE:
-                return PotionConfig.getInstance().concoctionsIngredientsTierFive;
-            case FOUR:
-                return PotionConfig.getInstance().concoctionsIngredientsTierFour;
-            case THREE:
-                return PotionConfig.getInstance().concoctionsIngredientsTierThree;
-            case TWO:
-                return PotionConfig.getInstance().concoctionsIngredientsTierTwo;
-            default:
-                return PotionConfig.getInstance().concoctionsIngredientsTierOne;
-        }
+        return PotionConfig.getInstance().getIngredients(getTier());
     }
 
     public String getIngredientList() {
         StringBuilder list = new StringBuilder();
 
         for (ItemStack ingredient : getIngredients()) {
-            String string = StringUtils.getPrettyItemString(ingredient.getType()) + (ingredient.getDurability() != 0 ? ":" + ingredient.getDurability() : "");
+            short durability = ingredient.getDurability();
+
+            String string = StringUtils.getPrettyItemString(ingredient.getType()) + (durability != 0 ? ":" + durability : "");
+
             if (string.equals("Long Grass:2")) {
                 string = "Fern";
             }
@@ -80,17 +48,20 @@ public class AlchemyManager extends SkillManager {
                 string = "Pufferfish";
             }
 
-            list.append(", " + string);
+            list.append(", ").append(string);
         }
+
         return list.substring(2);
     }
 
-    public double getBrewSpeed() {
-        return Alchemy.calculateBrewSpeed(getSkillLevel());
-    }
+    public double calculateBrewSpeed(boolean isLucky) {
+        int skillLevel = getSkillLevel();
+
+        if (skillLevel < Alchemy.catalysisUnlockLevel) {
+            return Alchemy.catalysisMinSpeed;
+        }
 
-    public double getBrewSpeedLucky() {
-        return LUCKY_MODIFIER * Alchemy.calculateBrewSpeed(getSkillLevel());
+        return Math.min(Alchemy.catalysisMaxSpeed, Alchemy.catalysisMinSpeed + (Alchemy.catalysisMaxSpeed - Alchemy.catalysisMinSpeed) * (skillLevel - Alchemy.catalysisUnlockLevel) / (Alchemy.catalysisMaxBonusLevel - Alchemy.catalysisUnlockLevel)) * (isLucky ? LUCKY_MODIFIER : 1.0);
     }
 
     public void handlePotionBrewSuccesses(int amount) {

+ 64 - 196
src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyPotionBrewer.java

@@ -8,10 +8,6 @@ import org.bukkit.block.BrewingStand;
 import org.bukkit.entity.HumanEntity;
 import org.bukkit.entity.Player;
 import org.bukkit.event.inventory.ClickType;
-import org.bukkit.event.inventory.InventoryClickEvent;
-import org.bukkit.event.inventory.InventoryDragEvent;
-import org.bukkit.event.inventory.InventoryMoveItemEvent;
-import org.bukkit.event.inventory.InventoryType.SlotType;
 import org.bukkit.inventory.BrewerInventory;
 import org.bukkit.inventory.Inventory;
 import org.bukkit.inventory.InventoryView;
@@ -27,60 +23,54 @@ import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 
 public final class AlchemyPotionBrewer {
-    private final static int[] BOTTLE_SLOTS = new int[]{0, 1, 2};
-    private final static int INGREDIENT_SLOT = 3;
-
     public static boolean isValidBrew(Player player, ItemStack[] contents) {
-        if (!isValidIngredient(player, contents[INGREDIENT_SLOT])) {
+        if (!isValidIngredient(player, contents[Alchemy.INGREDIENT_SLOT])) {
             return false;
         }
 
-        for (int bottle : BOTTLE_SLOTS) {
-            if (contents[bottle] != null && contents[bottle].getType() == Material.POTION) {
-                AlchemyPotion potion = PotionConfig.getInstance().potionMap.get(contents[bottle].getDurability());
+        for (int i = 0; i < 3; i++) {
+            if (contents[i] == null || contents[i].getType() != Material.POTION) {
+                continue;
+            }
 
-                if (getChildPotion(potion, contents[INGREDIENT_SLOT]) != null) {
-                    return true;
-                }
+            if (getChildPotion(PotionConfig.getInstance().getPotion(contents[i].getDurability()), contents[Alchemy.INGREDIENT_SLOT]) != null) {
+                return true;
             }
         }
 
         return false;
-
     }
 
     private static AlchemyPotion getChildPotion(AlchemyPotion potion, ItemStack ingredient) {
         if (potion != null && potion.getChildDataValue(ingredient) != -1) {
-            return PotionConfig.getInstance().potionMap.get(potion.getChildDataValue(ingredient));
+            return PotionConfig.getInstance().getPotion(potion.getChildDataValue(ingredient));
         }
 
         return null;
     }
 
-    private static boolean isEmpty(ItemStack item) {
+    public static boolean isEmpty(ItemStack item) {
         return item == null || item.getType() == Material.AIR || item.getAmount() == 0;
     }
 
     private static boolean removeIngredient(BrewerInventory inventory, Player player) {
-        ItemStack ingredient = inventory.getIngredient();
+        ItemStack ingredient = inventory.getIngredient().clone();
 
         if (isEmpty(ingredient) || !isValidIngredient(player, ingredient)) {
             return false;
         }
         else if (ingredient.getAmount() <= 1) {
-            inventory.setItem(INGREDIENT_SLOT, null);
-
+            inventory.setIngredient(null);
             return true;
         }
         else {
             ingredient.setAmount(ingredient.getAmount() - 1);
-            inventory.setItem(INGREDIENT_SLOT, ingredient);
-
+            inventory.setIngredient(ingredient);
             return true;
         }
     }
 
-    private static boolean isValidIngredient(Player player, ItemStack item) {
+    public static boolean isValidIngredient(Player player, ItemStack item) {
         if (isEmpty(item)) {
             return false;
         }
@@ -95,28 +85,7 @@ public final class AlchemyPotionBrewer {
     }
 
     private static List<ItemStack> getValidIngredients(Player player) {
-        if (player == null || !Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CONCOCTIONS)) {
-            return PotionConfig.getInstance().concoctionsIngredientsTierOne;
-        }
-
-        switch (UserManager.getPlayer(player).getAlchemyManager().getTier()) {
-            case 8:
-                return PotionConfig.getInstance().concoctionsIngredientsTierEight;
-            case 7:
-                return PotionConfig.getInstance().concoctionsIngredientsTierSeven;
-            case 6:
-                return PotionConfig.getInstance().concoctionsIngredientsTierSix;
-            case 5:
-                return PotionConfig.getInstance().concoctionsIngredientsTierFive;
-            case 4:
-                return PotionConfig.getInstance().concoctionsIngredientsTierFour;
-            case 3:
-                return PotionConfig.getInstance().concoctionsIngredientsTierThree;
-            case 2:
-                return PotionConfig.getInstance().concoctionsIngredientsTierTwo;
-            default:
-                return PotionConfig.getInstance().concoctionsIngredientsTierOne;
-        }
+        return PotionConfig.getInstance().getIngredients((player == null || !Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CONCOCTIONS)) ? 1 : UserManager.getPlayer(player).getAlchemyManager().getTier());
     }
 
     public static void finishBrewing(BlockState brewingStand, Player player, boolean forced) {
@@ -131,13 +100,15 @@ public final class AlchemyPotionBrewer {
             return;
         }
 
-        for (int bottle : BOTTLE_SLOTS) {
-            if (!isEmpty(inventory.getItem(bottle)) && PotionConfig.getInstance().potionMap.containsKey(inventory.getItem(bottle).getDurability())) {
-                AlchemyPotion input = PotionConfig.getInstance().potionMap.get(inventory.getItem(bottle).getDurability());
-                AlchemyPotion output = PotionConfig.getInstance().potionMap.get(input.getChildDataValue(ingredient));
+        for (int i = 0; i < 3; i++) {
+            ItemStack item = inventory.getItem(i);
+
+            if (!isEmpty(item) && PotionConfig.getInstance().isValidPotion(item)) {
+                AlchemyPotion input = PotionConfig.getInstance().getPotion(item.getDurability());
+                AlchemyPotion output = PotionConfig.getInstance().getPotion(input.getChildDataValue(ingredient));
 
                 if (output != null) {
-                    inventory.setItem(bottle, output.toItemStack(inventory.getItem(bottle).getAmount()).clone());
+                    inventory.setItem(i, output.toItemStack(item.getAmount()).clone());
 
                     if (player != null) {
                         UserManager.getPlayer(player).getAlchemyManager().handlePotionBrewSuccesses(1);
@@ -151,18 +122,36 @@ public final class AlchemyPotionBrewer {
         }
     }
 
+    public static boolean transferItems(InventoryView view, int fromSlot, int toSlot, ClickType click) {
+        boolean success = false;
+
+        if (click.isLeftClick()) {
+            success = transferOneItem(view, fromSlot, toSlot);
+        }
+        else if (click.isRightClick()) {
+            success = transferItems(view, fromSlot, toSlot);
+        }
+
+        return success;
+    }
+
     private static boolean transferOneItem(InventoryView view, int fromSlot, int toSlot) {
         ItemStack from = view.getItem(fromSlot).clone();
         ItemStack to = view.getItem(toSlot).clone();
+        boolean emptyFrom = isEmpty(from);
 
-        if (isEmpty(from)) {
+        if (emptyFrom) {
             return false;
         }
-        else if (!isEmpty(to) && from.getAmount() >= from.getType().getMaxStackSize()) {
+
+        boolean emptyTo = isEmpty(to);
+        int fromAmount = from.getAmount();
+
+        if (!emptyTo && fromAmount >= from.getType().getMaxStackSize()) {
             return false;
         }
-        else if (isEmpty(to) || from.isSimilar(to)) {
-            if (isEmpty(to)) {
+        else if (emptyTo || from.isSimilar(to)) {
+            if (emptyTo) {
                 to = from.clone();
                 to.setAmount(1);
             }
@@ -170,9 +159,9 @@ public final class AlchemyPotionBrewer {
                 to.setAmount(to.getAmount() + 1);
             }
 
-            from.setAmount(from.getAmount() - 1);
-            view.setItem(toSlot, isEmpty(to) ? null : to);
-            view.setItem(fromSlot, isEmpty(from) ? null : from);
+            from.setAmount(fromAmount - 1);
+            view.setItem(toSlot, emptyTo ? null : to);
+            view.setItem(fromSlot, emptyFrom ? null : from);
 
             return true;
         }
@@ -184,171 +173,50 @@ public final class AlchemyPotionBrewer {
      * Transfer items between two ItemStacks, returning the leftover status
      */
     private static boolean transferItems(InventoryView view, int fromSlot, int toSlot) {
-        if (isEmpty(view.getItem(fromSlot))) {
+        ItemStack from = view.getItem(fromSlot).clone();
+        ItemStack to = view.getItem(toSlot).clone();
+
+        if (isEmpty(from)) {
             return false;
         }
-        else if (isEmpty(view.getItem(toSlot))) {
-            view.setItem(toSlot, view.getItem(fromSlot).clone());
+        else if (isEmpty(to)) {
+            view.setItem(toSlot, from);
             view.setItem(fromSlot, null);
 
             return true;
         }
-        else if (view.getItem(fromSlot).isSimilar(view.getItem(toSlot))) {
-            if (view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount() > view.getItem(toSlot).getType().getMaxStackSize()) {
-                int left = view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount() - view.getItem(toSlot).getType().getMaxStackSize();
+        else if (from.isSimilar(to)) {
+            int fromAmount = from.getAmount();
+            int toAmount = to.getAmount();
+            int maxSize = to.getType().getMaxStackSize();
 
-                ItemStack to = new ItemStack(view.getItem(toSlot));
-                to.setAmount(to.getType().getMaxStackSize());
+            if (fromAmount + toAmount > maxSize) {
+                int left = fromAmount + toAmount - maxSize;
+
+                to.setAmount(maxSize);
                 view.setItem(toSlot, to);
 
-                ItemStack from = new ItemStack(view.getItem(fromSlot));
                 from.setAmount(left);
                 view.setItem(fromSlot, from);
 
                 return true;
             }
 
-            ItemStack to = new ItemStack(view.getItem(toSlot));
-            to.setAmount(view.getItem(fromSlot).getAmount() + view.getItem(toSlot).getAmount());
+            to.setAmount(fromAmount + toAmount);
             view.setItem(fromSlot, null);
             view.setItem(toSlot, to);
 
             return true;
         }
-        return false;
-    }
-
-    public static void handleInventoryClick(InventoryClickEvent event) {
-        Player player = event.getWhoClicked() instanceof Player ? (Player) event.getWhoClicked() : null;
-        BrewingStand brewingStand = (BrewingStand) event.getInventory().getHolder();
-
-        ItemStack cursor = event.getCursor();
-        ItemStack clicked = event.getCurrentItem();
-
-        if (clicked != null && clicked.getType() == Material.POTION) {
-            scheduleCheck(player, brewingStand);
-
-            return;
-        }
-
-        if (event.isShiftClick()) {
-            if (event.getSlotType() == SlotType.FUEL) {
-                scheduleCheck(player, brewingStand);
-
-                return;
-            }
-            else if (event.getSlotType() == SlotType.CONTAINER || event.getSlotType() == SlotType.QUICKBAR) {
-                if (isValidIngredient(player, clicked)) {
-                    if (event.isLeftClick()) {
-                        transferItems(event.getView(), event.getRawSlot(), INGREDIENT_SLOT);
-                    }
-                    else if (event.isRightClick()) {
-                        transferOneItem(event.getView(), event.getRawSlot(), INGREDIENT_SLOT);
-                    }
-
-                    event.setCancelled(true);
-
-                    scheduleUpdate(brewingStand.getInventory());
-                    scheduleCheck(player, brewingStand);
-
-                    return;
-                }
-            }
-        }
-        else if (event.getRawSlot() == INGREDIENT_SLOT) {
-            if (isEmpty(cursor) && isEmpty(clicked)) {
-                if (event.getClick() == ClickType.NUMBER_KEY) {
-                    scheduleCheck(player, brewingStand);
-                    return;
-                }
-                return;
-            }
-            else if (isEmpty(cursor)) {
-                scheduleCheck(player, brewingStand);
-
-                return;
-            }
-            else if (isEmpty(clicked)) {
-                if (isValidIngredient(player, event.getCursor())) {
-                    if (event.getClick() == ClickType.LEFT || (event.getClick() == ClickType.RIGHT && event.getCursor().getAmount() == 1)) {
-                        event.setCancelled(true);
-
-                        event.setCurrentItem(event.getCursor().clone());
-                        event.setCursor(null);
-
-                        scheduleUpdate(brewingStand.getInventory());
-                        scheduleCheck(player, brewingStand);
-
-                        return;
-                    }
-                    else if (event.getClick() == ClickType.RIGHT) {
-                        event.setCancelled(true);
-
-                        ItemStack one = event.getCursor().clone();
-                        one.setAmount(1);
-
-                        ItemStack rest = event.getCursor().clone();
-                        rest.setAmount(event.getCursor().getAmount() - 1);
-
-                        event.setCurrentItem(one);
-                        event.setCursor(rest);
-
-                        scheduleUpdate(brewingStand.getInventory());
-                        scheduleCheck(player, brewingStand);
-
-                        return;
-                    }
-                }
-                return;
-            }
-        }
-    }
 
-    public static void handleInventoryDrag(InventoryDragEvent event) {
-        Player player = event.getWhoClicked() instanceof Player ? (Player) event.getWhoClicked() : null;
-        BrewingStand brewingStand = (BrewingStand) event.getInventory().getHolder();
-
-        ItemStack cursor = event.getCursor();
-        ItemStack ingredient = brewingStand.getInventory().getIngredient();
-
-        if (!event.getInventorySlots().contains(INGREDIENT_SLOT)) {
-            return;
-        }
-
-        if (isEmpty(ingredient) || ingredient.isSimilar(cursor)) {
-            if (isValidIngredient(player, cursor)) {
-                // Not handled: dragging custom ingredients over ingredient slot (does not trigger any event)
-                scheduleCheck(player, brewingStand);
-
-                return;
-            }
-            else {
-                event.setCancelled(true);
-
-                scheduleUpdate(brewingStand.getInventory());
-
-                return;
-            }
-        }
-
-    }
-
-    public static void handleInventoryMoveItem(InventoryMoveItemEvent event) {
-        Player player = null;
-        BrewingStand brewingStand = (BrewingStand) event.getDestination().getHolder();
-
-        if (isValidIngredient(player, event.getItem())) {
-            scheduleCheck(player, brewingStand);
-
-            return;
-        }
+        return false;
     }
 
-    private static void scheduleCheck(Player player, BrewingStand brewingStand) {
+    public static void scheduleCheck(Player player, BrewingStand brewingStand) {
         new AlchemyBrewCheckTask(player, brewingStand).runTask(mcMMO.p);
     }
 
-    private static void scheduleUpdate(Inventory inventory) {
+    public static void scheduleUpdate(Inventory inventory) {
         for (HumanEntity humanEntity : inventory.getViewers()) {
             if (humanEntity instanceof Player) {
                 new PlayerUpdateInventoryTask((Player) humanEntity).runTask(mcMMO.p);