Sfoglia il codice sorgente

Update PR to changes in master

- Special thanks, instead of in dev team
- Fix formatting issues
  * Remove trailing whitespaces
  * Rename method names of event listeners
  * Check for negative instead of positive
- Added Alchemy skill guide
TfT_02 11 anni fa
parent
commit
6143003516

+ 1 - 1
Changelog.txt

@@ -8,7 +8,7 @@ Key:
   - Removal
 
 Version 1.4.08-dev
- + Added new Skill - Alchemy!
+ + Added a new skill; Alchemy. Special thanks to EasyMFnE for creating this!
  + Added SecondaryAbilityType enum, and new SecondaryAbilityWeightedActivationCheckEvent, fired when a secondary ability checkes its activation chances
  + Added the possibility to gain experience when using Fishing "Shake"
  + Added config options to disable various sound effects

+ 4 - 1
README.md

@@ -31,12 +31,15 @@ mcMMO is currently developed by a team of individuals from all over the world.
 (https://github.com/t00thpick1)
 [![riking](https://1.gravatar.com/avatar/aca9f37e569ac3a63929920035a91ba4.png)]
 (https://github.com/riking)
+
+### Special thanks
 [![EasyMFnE](https://www.gravatar.com/avatar/99c9a1fa3bbf957791ceac7b45daadb0.png)]
 (https://github.com/EasyMFnE)
+Added the Alchemy skill
 
 ## Compiling
 
-mcMMO uses Maven 3 to manage dependancies, packaging, and shading of necessary classes; Maven 3 is required to compile mcMMO.
+mcMMO uses Maven 3 to manage dependencies, packaging, and shading of necessary classes; Maven 3 is required to compile mcMMO.
 
 The typical command used to build mcMMO is: `mvn clean package install`
 

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

@@ -6,9 +6,11 @@ import java.util.List;
 import org.bukkit.entity.Player;
 
 import com.gmail.nossr50.config.AdvancedConfig;
+import com.gmail.nossr50.datatypes.skills.SecondaryAbility;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.skills.alchemy.Alchemy.Tier;
+import com.gmail.nossr50.skills.alchemy.AlchemyManager;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.player.UserManager;
 
@@ -27,11 +29,12 @@ public class AlchemyCommand extends SkillCommand {
         super(SkillType.ALCHEMY);
     }
 
-    protected String[] calculateAbilityDisplayValues(Player player, float skillValue, boolean isLucky) {
+    protected String[] calculateAbilityDisplayValues(Player player, boolean isLucky) {
+        AlchemyManager alchemyManager = UserManager.getPlayer(player).getAlchemyManager();
         String[] displayValues = new String[2];
 
-        displayValues[0] = decimal.format(UserManager.getPlayer(player).getAlchemyManager().getBrewSpeed()) + "x";
-        displayValues[1] = isLucky ? decimal.format(UserManager.getPlayer(player).getAlchemyManager().getBrewSpeedLucky()) + "x" : null;
+        displayValues[0] = decimal.format(alchemyManager.getBrewSpeed()) + "x";
+        displayValues[1] = isLucky ? decimal.format(alchemyManager.getBrewSpeedLucky()) + "x" : null;
 
         return displayValues;
     }
@@ -40,29 +43,30 @@ public class AlchemyCommand extends SkillCommand {
     protected void dataCalculations(Player player, float skillValue, boolean isLucky) {
         // CATALYSIS
         if (canCatalysis) {
-            String[] catalysisStrings = calculateAbilityDisplayValues(player, skillValue, isLucky);
+            String[] catalysisStrings = calculateAbilityDisplayValues(player, isLucky);
             brewSpeed = catalysisStrings[0];
             brewSpeedLucky = catalysisStrings[1];
         }
 
         // CONCOCTIONS
         if (canConcoctions) {
-            tier = UserManager.getPlayer(player).getAlchemyManager().getTier();
-            ingredientCount = UserManager.getPlayer(player).getAlchemyManager().getIngredients().size();
-            ingredientList = UserManager.getPlayer(player).getAlchemyManager().getIngredientList();
+            AlchemyManager alchemyManager = UserManager.getPlayer(player).getAlchemyManager();
+            tier = alchemyManager.getTier();
+            ingredientCount = alchemyManager.getIngredients().size();
+            ingredientList = alchemyManager.getIngredientList();
         }
     }
 
     @Override
     protected void permissionsCheck(Player player) {
-        canCatalysis = Permissions.catalysis(player);
-        canConcoctions = Permissions.concoctions(player);
+        canCatalysis = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CATALYSIS);
+        canConcoctions = Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CONCOCTIONS);
     }
 
     @Override
     protected List<String> effectsDisplay() {
         List<String> messages = new ArrayList<String>();
-        
+
         if (canCatalysis) {
             messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Alchemy.Effect.0"), LocaleLoader.getString("Alchemy.Effect.1")));
         }
@@ -70,7 +74,7 @@ public class AlchemyCommand extends SkillCommand {
         if (canConcoctions) {
             messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Alchemy.Effect.2"), LocaleLoader.getString("Alchemy.Effect.3")));
         }
-        
+
         return messages;
     }
 

+ 17 - 13
src/main/java/com/gmail/nossr50/config/potion/PotionConfig.java

@@ -41,7 +41,7 @@ public class PotionConfig extends ConfigLoader {
 
         return instance;
     }
-    
+
     @Override
     protected void loadKeys() {
         loadConcoctions();
@@ -59,7 +59,7 @@ public class PotionConfig extends ConfigLoader {
         loadConcoctionsTier(concoctionsIngredientsTierSix, concoctionSection.getStringList("Tier_Six_Ingredients"));
         loadConcoctionsTier(concoctionsIngredientsTierSeven, concoctionSection.getStringList("Tier_Seven_Ingredients"));
         loadConcoctionsTier(concoctionsIngredientsTierEight, concoctionSection.getStringList("Tier_Eight_Ingredients"));
-        
+
         concoctionsIngredientsTierTwo.addAll(concoctionsIngredientsTierOne);
         concoctionsIngredientsTierThree.addAll(concoctionsIngredientsTierTwo);
         concoctionsIngredientsTierFour.addAll(concoctionsIngredientsTierThree);
@@ -73,7 +73,7 @@ public class PotionConfig extends ConfigLoader {
         if (ingredientStrings != null && ingredientStrings.size() > 0) {
             for (String ingredientString : ingredientStrings) {
                 ItemStack ingredient = loadIngredient(ingredientString);
-                
+
                 if (ingredient != null) {
                     ingredientList.add(ingredient);
                 }
@@ -91,7 +91,7 @@ public class PotionConfig extends ConfigLoader {
 
         for (String dataValue : potionSection.getKeys(false)) {
             AlchemyPotion potion = loadPotion(potionSection.getConfigurationSection(dataValue));
-            
+
             if (potion != null) {
                 potionMap.put(potion.getDataValue(), potion);
                 pass++;
@@ -100,30 +100,31 @@ public class PotionConfig extends ConfigLoader {
                 fail++;
             }
         }
-        
-        mcMMO.p.getLogger().info("Loaded " + pass + " Alchemy potions, skipped " + fail + ".");
+
+        mcMMO.p.debug("Loaded " + pass + " Alchemy potions, skipped " + fail + ".");
     }
 
     /**
      * Parse a ConfigurationSection representing a AlchemyPotion.
      * Returns null if input cannot be parsed.
-     * 
+     *
      * @param potion_section ConfigurationSection to be parsed.
+     *
      * @return Parsed AlchemyPotion.
      */
     private AlchemyPotion loadPotion(ConfigurationSection potion_section) {
         try {
             short dataValue = Short.parseShort(potion_section.getName());
-            
+
             String name = potion_section.getString("Name");
-            
+
             List<String> lore = new ArrayList<String>();
             if (potion_section.contains("Lore")) {
                 for (String line : potion_section.getStringList("Lore")) {
                     lore.add(line);
                 }
             }
-            
+
             List<PotionEffect> effects = new ArrayList<PotionEffect>();
             if (potion_section.contains("Effects")) {
                 for (String effect : potion_section.getStringList("Effects")) {
@@ -167,22 +168,25 @@ public class PotionConfig extends ConfigLoader {
      * Parse a string representation of an ingredient.
      * Format: '&lt;MATERIAL&gt;[:data]'
      * Returns null if input cannot be parsed.
-     * 
+     *
      * @param ingredient String representing an ingredient.
+     *
      * @return Parsed ingredient.
      */
     private ItemStack loadIngredient(String ingredient) {
         if (ingredient == null || ingredient.isEmpty()) {
             return null;
         }
+
         String[] parts = ingredient.split(":");
-        
+
         Material material = parts.length > 0 ? Material.getMaterial(parts[0]) : null;
         short data = parts.length > 1 ? Short.parseShort(parts[1]) : 0;
-        
+
         if (material != null) {
             return new ItemStack(material, 1, data);
         }
+
         return null;
     }
 }

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

@@ -6,6 +6,10 @@ public enum SecondaryAbility {
     GRACEFUL_ROLL,
     ROLL,
 
+    /* ALCHEMY */
+    CATALYSIS,
+    CONCOCTIONS,
+
     /* ARCHERY */
     DAZE,
     RETRIEVE,

+ 1 - 1
src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java

@@ -36,7 +36,7 @@ import com.google.common.collect.ImmutableList;
 
 public enum SkillType {
     ACROBATICS(AcrobaticsManager.class, Color.WHITE, ImmutableList.of(SecondaryAbility.DODGE, SecondaryAbility.GRACEFUL_ROLL, SecondaryAbility.ROLL)),
-    ALCHEMY(AlchemyManager.class, Color.FUCHSIA, null),
+    ALCHEMY(AlchemyManager.class, Color.FUCHSIA, ImmutableList.of(SecondaryAbility.CATALYSIS, SecondaryAbility.CONCOCTIONS)),
     ARCHERY(ArcheryManager.class, Color.MAROON, ImmutableList.of(SecondaryAbility.DAZE, SecondaryAbility.RETRIEVE, SecondaryAbility.SKILL_SHOT)),
     AXES(AxesManager.class, Color.AQUA, AbilityType.SKULL_SPLITTER, ToolType.AXE, ImmutableList.of(SecondaryAbility.ARMOR_IMPACT, SecondaryAbility.AXE_MASTERY, SecondaryAbility.CRITICAL_HIT, SecondaryAbility.GREATER_IMPACT)),
     EXCAVATION(ExcavationManager.class, Color.fromRGB(139, 69, 19), AbilityType.GIGA_DRILL_BREAKER, ToolType.SHOVEL, ImmutableList.of(SecondaryAbility.EXCAVATION_TREASURE_HUNTER)),

+ 1 - 1
src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerBrewEvent.java

@@ -10,7 +10,7 @@ import com.gmail.nossr50.events.skills.McMMOPlayerSkillEvent;
 
 public class McMMOPlayerBrewEvent extends McMMOPlayerSkillEvent implements Cancellable {
     private Block brewingStand;
-    
+
     private boolean cancelled;
 
     public McMMOPlayerBrewEvent(Player player, Block brewingStand) {

+ 1 - 1
src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerCatalysisEvent.java

@@ -8,7 +8,7 @@ import com.gmail.nossr50.events.skills.McMMOPlayerSkillEvent;
 
 public class McMMOPlayerCatalysisEvent extends McMMOPlayerSkillEvent implements Cancellable {
     private double speed;
-    
+
     private boolean cancelled;
 
     public McMMOPlayerCatalysisEvent(Player player, double speed) {

+ 9 - 8
src/main/java/com/gmail/nossr50/listeners/EntityListener.java

@@ -598,20 +598,21 @@ public class EntityListener implements Listener {
 
     /**
      * Handle PotionSplash events in order to fix broken Splash Potion of Saturation.
-     * 
+     *
      * @param event The event to process
      */
     @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
     public void onPotionSplash(PotionSplashEvent event) {
         for (PotionEffect effect : ((PotionMeta) event.getEntity().getItem().getItemMeta()).getCustomEffects()) {
-            // (effect.getType() == PotionEffectType.SATURATION) is seemingly broken, so we use deprecated method for now.
-            if (effect.getType().getId() == 23) {
-                for (LivingEntity entity : event.getAffectedEntities()) {
-                    int duration = (int) (effect.getDuration() * event.getIntensity(entity));
-                    entity.addPotionEffect(new PotionEffect(effect.getType(), duration, effect.getAmplifier(), effect.isAmbient()));
-                }
+            // (effect.getType() != PotionEffectType.SATURATION) is seemingly broken, so we use deprecated method for now.
+            if (effect.getType().getId() != 23) {
+                return;
+            }
+
+            for (LivingEntity entity : event.getAffectedEntities()) {
+                int duration = (int) (effect.getDuration() * event.getIntensity(entity));
+                entity.addPotionEffect(new PotionEffect(effect.getType(), duration, effect.getAmplifier(), effect.isAmbient()));
             }
-            return;
         }
     }
 }

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

@@ -136,8 +136,8 @@ public class InventoryListener implements Listener {
         event.setExpToDrop(UserManager.getPlayer(player).getSmeltingManager().vanillaXPBoost(event.getExpToDrop()));
     }
 
-    @EventHandler(ignoreCancelled = true)
-    public void onAlchemyClickEvent(InventoryClickEvent event) {
+    @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+    public void onInventoryClickEventNormal(InventoryClickEvent event) {
         if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) {
             return;
         }
@@ -149,8 +149,8 @@ public class InventoryListener implements Listener {
         AlchemyPotionBrewer.handleInventoryClick(event);
     }
 
-    @EventHandler(ignoreCancelled = true)
-    public void onAlchemyDragEvent(InventoryDragEvent event) {
+    @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+    public void onInventoryDragEvent(InventoryDragEvent event) {
         if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) {
             return;
         }
@@ -162,13 +162,13 @@ public class InventoryListener implements Listener {
         AlchemyPotionBrewer.handleInventoryDrag(event);
     }
 
-    @EventHandler(ignoreCancelled = true)
-    public void onAlchemyMoveItemEvent(InventoryMoveItemEvent event) {
+    @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+    public void onInventoryMoveItemEvent(InventoryMoveItemEvent event) {
         if (event.getDestination().getType() != InventoryType.BREWING || !(event.getDestination().getHolder() instanceof BrewingStand)) {
             return;
         }
 
-        if (Config.getInstance().getPreventHopperTransfer()  && event.getItem() != null && event.getItem().getType() != Material.POTION) {
+        if (Config.getInstance().getPreventHopperTransfer() && event.getItem() != null && event.getItem().getType() != Material.POTION) {
             event.setCancelled(true);
             return;
         }

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

@@ -6,6 +6,7 @@ import org.bukkit.block.BrewingStand;
 import org.bukkit.entity.Player;
 import org.bukkit.scheduler.BukkitRunnable;
 
+import com.gmail.nossr50.datatypes.skills.SecondaryAbility;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.events.skills.alchemy.McMMOPlayerBrewEvent;
@@ -32,7 +33,7 @@ public class AlchemyBrewTask extends BukkitRunnable {
         brewSpeed = DEFAULT_BREW_SPEED;
         brewTimer = DEFAULT_BREW_TICKS;
         
-        if (player != null && !Misc.isNPCEntity(player) && Permissions.catalysis(player)) {
+        if (player != null && !Misc.isNPCEntity(player) && Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CATALYSIS)) {
             double catalysis = UserManager.getPlayer(player).getAlchemyManager().getBrewSpeed();
             
             if (Permissions.lucky(player, SkillType.ALCHEMY)) {

+ 10 - 13
src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java

@@ -2,13 +2,11 @@ package com.gmail.nossr50.skills.alchemy;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Map.Entry;
 
 import org.bukkit.block.Block;
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.AdvancedConfig;
-import com.gmail.nossr50.config.experience.ExperienceConfig;
 import com.gmail.nossr50.runnables.skills.AlchemyBrewTask;
 
 public final class Alchemy {
@@ -31,7 +29,7 @@ public final class Alchemy {
         public int toNumerical() {
             return numerical;
         }
-        
+
         public static Tier fromNumerical(int numerical) {
             for (Tier tier : Tier.values()) {
                 if (tier.toNumerical() == numerical) {
@@ -45,22 +43,21 @@ public final class Alchemy {
             return AdvancedConfig.getInstance().getConcoctionsTierLevel(this);
         }
     }
-    
+
     public static int    catalysisUnlockLevel   = AdvancedConfig.getInstance().getCatalysisUnlockLevel();
     public static int    catalysisMaxBonusLevel = AdvancedConfig.getInstance().getCatalysisMaxBonusLevel();
     public static double catalysisMinSpeed      = AdvancedConfig.getInstance().getCatalysisMinSpeed();
     public static double catalysisMaxSpeed      = AdvancedConfig.getInstance().getCatalysisMaxSpeed();
 
-    public static double potionXp = ExperienceConfig.getInstance().getPotionXP();
-    
     public static Map<Block, AlchemyBrewTask> brewingStandMap = new HashMap<Block, AlchemyBrewTask>();
 
-    private 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) {
@@ -70,14 +67,14 @@ public final class Alchemy {
 
         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.getLogger().info("Completing " + brewingStandMap.size() + " unfinished Alchemy brews.");
-        for (Entry<Block, AlchemyBrewTask> entry : brewingStandMap.entrySet()) {
-            entry.getValue().finishImmediately();
+        mcMMO.p.debug("Completing " + brewingStandMap.size() + " unfinished Alchemy brews.");
+        for (AlchemyBrewTask alchemyBrewTask : brewingStandMap.values()) {
+            alchemyBrewTask.finishImmediately();
         }
     }
 }

+ 18 - 6
src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java

@@ -7,6 +7,7 @@ import org.bukkit.inventory.ItemStack;
 import com.gmail.nossr50.config.experience.ExperienceConfig;
 import com.gmail.nossr50.config.potion.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;
@@ -20,7 +21,11 @@ public class AlchemyManager extends SkillManager {
     }
 
     public boolean canCatalysis() {
-        return Permissions.catalysis(getPlayer());
+        return Permissions.secondaryAbilityEnabled(getPlayer(), SecondaryAbility.CATALYSIS);
+    }
+
+    public boolean canConcoctions() {
+        return Permissions.secondaryAbilityEnabled(getPlayer(), SecondaryAbility.CONCOCTIONS);
     }
 
     public boolean canUseIngredient(ItemStack item) {
@@ -66,21 +71,28 @@ public class AlchemyManager extends SkillManager {
     public String getIngredientList() {
         String list = "";
         for (ItemStack ingredient : getIngredients()) {
-            list += ", " + StringUtils.getPrettyItemString(ingredient.getType()) + (ingredient.getDurability() != 0 ? ":" + ingredient.getDurability() : "");
+            String string = StringUtils.getPrettyItemString(ingredient.getType()) + (ingredient.getDurability() != 0 ? ":" + ingredient.getDurability() : "");
+            if (string.equals("LONG_GRASS:2")) {
+                string = "Fern";
+            }
+            else if (string.equals("RAW_FISH:3")) {
+                string = "Pufferfish";
+            }
+
+            list += ", " + string;
         }
         return list.substring(2);
     }
 
-    public double getBrewSpeed() { 
+    public double getBrewSpeed() {
         return Alchemy.calculateBrewSpeed(getSkillLevel());
     }
-    
+
     public double getBrewSpeedLucky() {
         return LUCKY_MODIFIER * Alchemy.calculateBrewSpeed(getSkillLevel());
     }
-    
+
     public void handlePotionBrewSuccesses(int amount) {
         applyXpGain((float) (ExperienceConfig.getInstance().getPotionXP() * amount));
     }
-
 }

+ 95 - 87
src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyPotionBrewer.java

@@ -21,36 +21,40 @@ import org.bukkit.inventory.ItemStack;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.potion.PotionConfig;
 import com.gmail.nossr50.datatypes.AlchemyPotion;
+import com.gmail.nossr50.datatypes.skills.SecondaryAbility;
 import com.gmail.nossr50.runnables.PlayerUpdateInventoryTask;
 import com.gmail.nossr50.runnables.skills.AlchemyBrewCheckTask;
 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[] 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])) {
-            for (int bottle : BOTTLE_SLOTS) {
-                if (contents[bottle] != null && contents[bottle].getType() == Material.POTION) {
-                    AlchemyPotion potion = PotionConfig.getInstance().potionMap.get(contents[bottle].getDurability());
-                    
-                    if (getChildPotion(potion, contents[INGREDIENT_SLOT]) != null) {
-                        return true;
-                    }
+        if (!isValidIngredient(player, contents[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());
+
+                if (getChildPotion(potion, contents[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 null;
     }
 
@@ -60,19 +64,19 @@ public final class AlchemyPotionBrewer {
 
     private static boolean removeIngredient(BrewerInventory inventory, Player player) {
         ItemStack ingredient = inventory.getIngredient();
-        
+
         if (isEmpty(ingredient) || !isValidIngredient(player, ingredient)) {
             return false;
         }
         else if (ingredient.getAmount() <= 1) {
             inventory.setItem(INGREDIENT_SLOT, null);
-            
+
             return true;
         }
         else {
             ingredient.setAmount(ingredient.getAmount() - 1);
             inventory.setItem(INGREDIENT_SLOT, ingredient);
-            
+
             return true;
         }
     }
@@ -81,21 +85,21 @@ public final class AlchemyPotionBrewer {
         if (isEmpty(item)) {
             return false;
         }
-        
+
         for (ItemStack ingredient : getValidIngredients(player)) {
             if (item.isSimilar(ingredient)) {
                 return true;
             }
         }
-        
+
         return false;
     }
 
     private static List<ItemStack> getValidIngredients(Player player) {
-        if (player == null || !Permissions.concoctions(player)) {
+        if (player == null || !Permissions.secondaryAbilityEnabled(player, SecondaryAbility.CONCOCTIONS)) {
             return PotionConfig.getInstance().concoctionsIngredientsTierOne;
         }
-        
+
         switch (UserManager.getPlayer(player).getAlchemyManager().getTier()) {
             case 5:
                 return PotionConfig.getInstance().concoctionsIngredientsTierFive;
@@ -114,38 +118,38 @@ public final class AlchemyPotionBrewer {
         if (!(brewingStand.getState() instanceof BrewingStand)) {
             return;
         }
-        
+
         BrewerInventory inventory = ((BrewingStand) brewingStand.getState()).getInventory();
         ItemStack ingredient = inventory.getIngredient() == null ? null : inventory.getIngredient().clone();
-        
-        if (removeIngredient(inventory, player)) {
-            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));
-                    
-                    if (output != null) {
-                        inventory.setItem(bottle, output.toItemStack(inventory.getItem(bottle).getAmount()).clone());
-                        
-                        if (player != null) {
-                            UserManager.getPlayer(player).getAlchemyManager().handlePotionBrewSuccesses(1);
-                        }
+
+        if (!removeIngredient(inventory, player)) {
+            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));
+
+                if (output != null) {
+                    inventory.setItem(bottle, output.toItemStack(inventory.getItem(bottle).getAmount()).clone());
+
+                    if (player != null) {
+                        UserManager.getPlayer(player).getAlchemyManager().handlePotionBrewSuccesses(1);
                     }
                 }
             }
-            
-            if (!forced) {
-                scheduleUpdate(inventory);
-            }
-            
-            return;
+        }
+
+        if (!forced) {
+            scheduleUpdate(inventory);
         }
     }
 
     private static boolean transferOneItem(InventoryView view, int fromSlot, int toSlot) {
         ItemStack from = view.getItem(fromSlot).clone();
         ItemStack to = view.getItem(toSlot).clone();
-        
+
         if (isEmpty(from)) {
             return false;
         }
@@ -160,14 +164,14 @@ public final class AlchemyPotionBrewer {
             else {
                 to.setAmount(to.getAmount() + 1);
             }
-            
+
             from.setAmount(from.getAmount() - 1);
             view.setItem(toSlot, isEmpty(to) ? null : to);
             view.setItem(fromSlot, isEmpty(from) ? null : from);
-            
+
             return true;
         }
-        
+
         return false;
     }
 
@@ -181,29 +185,29 @@ public final class AlchemyPotionBrewer {
         else if (isEmpty(view.getItem(toSlot))) {
             view.setItem(toSlot, view.getItem(fromSlot).clone());
             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();
-                
+
                 ItemStack to = new ItemStack(view.getItem(toSlot));
                 to.setAmount(to.getType().getMaxStackSize());
                 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());
             view.setItem(fromSlot, null);
             view.setItem(toSlot, to);
-            
+
             return true;
         }
         return false;
@@ -214,12 +218,13 @@ public final class AlchemyPotionBrewer {
     }
 
     private static void scheduleUpdate(Inventory inventory) {
-        for (HumanEntity he : inventory.getViewers()) {
-            if (he instanceof Player) {
-                scheduleUpdate((Player) he);
+        for (HumanEntity humanEntity : inventory.getViewers()) {
+            if (humanEntity instanceof Player) {
+                scheduleUpdate((Player) humanEntity);
             }
         }
     }
+
     private static void scheduleUpdate(Player player) {
         Bukkit.getScheduler().scheduleSyncDelayedTask(mcMMO.p, new PlayerUpdateInventoryTask(player));
     }
@@ -227,19 +232,19 @@ public final class AlchemyPotionBrewer {
     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) {
@@ -250,12 +255,12 @@ public final class AlchemyPotionBrewer {
                     else if (event.isRightClick()) {
                         transferOneItem(event.getView(), event.getRawSlot(), INGREDIENT_SLOT);
                     }
-                    
+
                     event.setCancelled(true);
-                    
+
                     scheduleUpdate(brewingStand.getInventory());
                     scheduleCheck(player, brewingStand);
-                    
+
                     return;
                 }
             }
@@ -266,37 +271,37 @@ public final class AlchemyPotionBrewer {
             }
             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;
                     }
                 }
@@ -308,41 +313,44 @@ public final class AlchemyPotionBrewer {
     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)) {
-            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;
-                }
+
+        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;
         }
         else {
             event.setCancelled(true);
-            
+
             return;
         }
     }

+ 11 - 2
src/main/resources/locale/locale_en_US.properties

@@ -40,7 +40,7 @@ Alchemy.Effect.3=Brew potions with more ingredients
 Alchemy.Listener=Alchemy:
 Alchemy.Ability.Locked.0=LOCKED UNTIL {0}+ SKILL (CATALYSIS)
 Alchemy.Catalysis.Speed=[[RED]]Brewing Speed: [[YELLOW]]{0}
-Alchemy.Concoctions.Rank=[[RED]]Concoctions Rank: [[YELLOW]]{0}
+Alchemy.Concoctions.Rank=[[RED]]Concoctions Rank: [[YELLOW]]{0}/{1}
 Alchemy.Concoctions.Ingredients=[[RED]]Ingredients [[[YELLOW]]{0}[[RED]]]: [[YELLOW]]{1}
 Alchemy.SkillName=ALCHEMY
 Alchemy.Skillup=[[YELLOW]]Alchemy skill increased by {0}. Total ({1})
@@ -421,7 +421,7 @@ Combat.TouchedFuzzy=[[DARK_RED]]Touched Fuzzy. Felt Dizzy.
 
 #COMMANDS
 ##generic
-mcMMO.Description=[[DARK_AQUA]]About the [[YELLOW]]mcMMO[[DARK_AQUA]] Project:,[[GOLD]]mcMMO is an [[RED]]open source[[GOLD]] RPG mod created in February 2011,[[GOLD]]by [[BLUE]]nossr50[[GOLD]]. The goal is to provide a quality RPG experience.,[[DARK_AQUA]]Tips:,[[GOLD]] - [[GREEN]]Use [[RED]]/mcmmo help[[GREEN]] to see commands,[[GOLD]] - [[GREEN]]Type [[RED]]/SKILLNAME[[GREEN]] to see detailed skill info,[[DARK_AQUA]]Developers:,[[GOLD]] - [[GREEN]]nossr50 [[BLUE]](Founder),[[GOLD]] - [[GREEN]]GJ [[BLUE]](Project Lead),[[GOLD]] - [[GREEN]]NuclearW [[BLUE]](Developer),[[GOLD]] - [[GREEN]]bm01 [[BLUE]](Developer),[[GOLD]] - [[GREEN]]TfT_02 [[BLUE]](Developer),[[GOLD]] - [[GREEN]]Glitchfinder [[BLUE]](Developer),[[GOLD]] - [[GREEN]]t00thpick1 [[BLUE]](Developer),[[GOLD]] - [[GREEN]]EasyMFnE [[BLUE]](Developer),[[DARK_AQUA]]Useful Links:,[[GOLD]] - [[GREEN]]https://github.com/mcMMO-Dev/mcMMO/issues[[GOLD]] Bug Reporting,[[GOLD]] - [[GREEN]]#mcmmo @ irc.esper.net[[GOLD]] IRC Chat,
+mcMMO.Description=[[DARK_AQUA]]About the [[YELLOW]]mcMMO[[DARK_AQUA]] Project:,[[GOLD]]mcMMO is an [[RED]]open source[[GOLD]] RPG mod created in February 2011,[[GOLD]]by [[BLUE]]nossr50[[GOLD]]. The goal is to provide a quality RPG experience.,[[DARK_AQUA]]Tips:,[[GOLD]] - [[GREEN]]Use [[RED]]/mcmmo help[[GREEN]] to see commands,[[GOLD]] - [[GREEN]]Type [[RED]]/SKILLNAME[[GREEN]] to see detailed skill info,[[DARK_AQUA]]Developers:,[[GOLD]] - [[GREEN]]nossr50 [[BLUE]](Founder),[[GOLD]] - [[GREEN]]GJ [[BLUE]](Project Lead),[[GOLD]] - [[GREEN]]NuclearW [[BLUE]](Developer),[[GOLD]] - [[GREEN]]bm01 [[BLUE]](Developer),[[GOLD]] - [[GREEN]]TfT_02 [[BLUE]](Developer),[[GOLD]] - [[GREEN]]Glitchfinder [[BLUE]](Developer),[[GOLD]] - [[GREEN]]t00thpick1 [[BLUE]](Developer),[[DARK_AQUA]]Useful Links:,[[GOLD]] - [[GREEN]]https://github.com/mcMMO-Dev/mcMMO/issues[[GOLD]] Bug Reporting,[[GOLD]] - [[GREEN]]#mcmmo @ irc.esper.net[[GOLD]] IRC Chat,
 Commands.addlevels.AwardAll.1=[[GREEN]]You were awarded {0} levels in all skills!
 Commands.addlevels.AwardAll.2=[[RED]]All skills have been modified for {0}.
 Commands.addlevels.AwardSkill.1=[[GREEN]]You were awarded {0} levels in {1}!
@@ -669,6 +669,15 @@ Guides.Acrobatics.Section.0=[[DARK_AQUA]]About Acrobatics:\n[[YELLOW]]Acrobatics
 Guides.Acrobatics.Section.1=[[DARK_AQUA]]How does Rolling work?\n[[YELLOW]]You have a passive chance when you take fall damage\n[[YELLOW]]to negate the damage done. You can hold the sneak button to\n[[YELLOW]]double your chances during the fall.\n[[YELLOW]]This triggers a Graceful Roll instead of a standard one.\n[[YELLOW]]Graceful Rolls are like regular rolls but are twice as likely to\n[[YELLOW]]occur and provide more damage safety than regular rolls.\n[[YELLOW]]Rolling chance is tied to your skill level
 Guides.Acrobatics.Section.2=[[DARK_AQUA]]How does Dodge work?\n[[YELLOW]]Dodge is a passive chance when you are\n[[YELLOW]]injured in combat to halve the damage taken.\n[[YELLOW]]It is tied to your skill level.
 
+##Alchemy
+Guides.Alchemy.Section.0=[[DARK_AQUA]]About Alchemy:\n[[YELLOW]]Alchemy is about brewing potions.\n[[YELLOW]]It provides a speed increase in the potion brew time, as well\n[[YELLOW]]as the addition of new (previously) unobtainable potions.\n\n\n[[DARK_AQUA]]XP GAIN:\n[[YELLOW]]To gain XP in this skill you need to brew potions.
+Guides.Alchemy.Section.1=[[DARK_AQUA]]How does Catalysis work?\n[[YELLOW]]Catalysis speeds of the brewing process, with a\n[[YELLOW]]max speed of 4x at level 1000.\n[[YELLOW]]This ability is unlocked at level 100 by default.
+Guides.Alchemy.Section.2=[[DARK_AQUA]]How does Concoctions work?\n[[YELLOW]]Concoctions allows brewing of more potions with custom ingredients.\n[[YELLOW]]Which special ingredients are unlocked is determined\n[[YELLOW]]by your Rank. There are 8 ranks to unlock.
+Guides.Alchemy.Section.3=[[DARK_AQUA]]Concotions tier 1 ingredients:\n[[YELLOW]]Blaze Powder, Fermented Spider Eye, Ghast Tear, Redstone,\n[[YELLOW]]Glowstone Dust, Sugar, Glistering Melon, Golden Carrot,\n[[YELLOW]]Magma Cream, Nether Wart, Spider Eye, Suplhur, Water Lily,\n[[YELLOW]]Pufferfish\n[[YELLOW]](Vanilla Potions)
+Guides.Alchemy.Section.4=[[DARK_AQUA]]Concotions tier 2 ingredients:\n[[YELLOW]]Carrot (Potion of Haste)\n[[YELLOW]]Slimeball (Potion of Dullness)\n\n[[DARK_AQUA]]Concotions tier 3 ingredients:\n[[YELLOW]]Quartz (Potion of Absorption)\n[[YELLOW]]Red Mushroom (Potion of Leaping)
+Guides.Alchemy.Section.5=[[DARK_AQUA]]Concotions tier 4 ingredients:\n[[YELLOW]]Apple (Potion of Health Boost)\n[[YELLOW]]Rotten Flesh (Potion of Hunger)\n\n[[DARK_AQUA]]Concotions tier 5 ingredients:\n[[YELLOW]]Brown Mushroom (Potion of Nausea)\n[[YELLOW]]Ink Sack (Potion of Blindness)
+Guides.Alchemy.Section.6=[[DARK_AQUA]]Concotions tier 6 ingredients:\n[[YELLOW]]Fern (Potion of Saturation)\n\n[[DARK_AQUA]]Concotions tier 7 ingredients:\n[[YELLOW]]Poisonous Potato (Potion of Decay)\n\n[[DARK_AQUA]]Concotions tier 8 ingredients:\n[[YELLOW]]Regular Golden Apple (Potion of Resistance)
+
 ##Archery
 Guides.Archery.Section.0=[[DARK_AQUA]]About Archery:\n[[YELLOW]]Archery is about shooting with your bow and arrow.\n[[YELLOW]]It provides various combat bonuses, such as a damage boost\n[[YELLOW]]that scales with your level and the ability to daze your\n[[YELLOW]]opponents in PvP. In addition to this, you can retrieve\n[[YELLOW]]some of your spent arrows from the corpses of your foes.\n\n\n[[DARK_AQUA]]XP GAIN:\n[[YELLOW]]To gain XP in this skill you need to shoot mobs or\n[[YELLOW]]other players.
 Guides.Archery.Section.1=[[DARK_AQUA]]How does Skill Shot work?\n[[YELLOW]]Skill Shot provides additional damage to your shots.\n[[YELLOW]]The bonus damage from Skill Shot increases as you\n[[YELLOW]]level in Archery.\n[[YELLOW]]With the default settings, your archery damage increases 10%\n[[YELLOW]]every 50 levels, to a maximum of 200% bonus damage.

+ 1 - 1
src/main/resources/plugin.yml

@@ -11,7 +11,7 @@ description: >
   in order to evaluate and balance the mechanics of mcMMO in every update.
 
 author: nossr50
-authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking, EasyMFnE]
+authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking]
 website: http://dev.bukkit.org/server-mods/mcmmo/
 
 main: com.gmail.nossr50.mcMMO

+ 3 - 2
src/main/resources/potions.yml

@@ -16,7 +16,8 @@ Concoctions:
         - SPIDER_EYE
         - SUGAR
         - SULPHUR
-        - WATER_LILY                        # TODO: 'RAW_FISH:3' (Minecraft 1.7)
+        - WATER_LILY
+        - 'RAW_FISH:3'
     Tier_Two_Ingredients:
         - CARROT_ITEM
         - SLIME_BALL
@@ -97,7 +98,7 @@ Potions:
             SPIDER_EYE: 4868            # Potion of Poison
             SUGAR: 258                  # Potion of Swiftness
             WATER_LILY: 3341            # Potion of Water Breathing (Minecraft 1.6)
-            # 'RAW_FISH:3': 3341          # TODO: Potion of Water Breathing (Minecraft 1.7)
+            'RAW_FISH:3': 3341          # Potion of Water Breathing (Minecraft 1.7)
 
     32: # Thick Potion
         Children: