diff --git a/Changelog.txt b/Changelog.txt index e82c8f3c7..e5728469d 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -8,6 +8,7 @@ Key: - Removal Version 1.4.08-dev + + Added new Skill - Alchemy! + 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 diff --git a/README.md b/README.md index 8a661fdad..935b14097 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Our latest development builds are available ~~[here](http://ci.mcmmo.info)~~. Unfortunately, the mcMMO site is down; a temporary dev build location is hosted [here](http://ci.ecocitycraft.com/job/mcMMO/). ### Brief Description -The goal of mcMMO is to take core Minecraft game mechanics and expand them into add an extensive and quality RPG experience. Everything in mcMMO has been carefully thought out and is constantly being improved upon. Currently, mcMMO adds thirteen unique skills to train and level in. Each of these skills is highly customizable through our configuration files, allowing server admins to tweak mcMMO to best suit the needs of his or her server. Know that the mcMMO team is dedicated to providing an ever-evolving experience, and that we carefully read all feedback and bug reports in order to evaluate and balance the mechanics of mcMMO in every update. +The goal of mcMMO is to take core Minecraft game mechanics and expand them into add an extensive and quality RPG experience. Everything in mcMMO has been carefully thought out and is constantly being improved upon. Currently, mcMMO adds fourteen unique skills to train and level in. Each of these skills is highly customizable through our configuration files, allowing server admins to tweak mcMMO to best suit the needs of his or her server. Know that the mcMMO team is dedicated to providing an ever-evolving experience, and that we carefully read all feedback and bug reports in order to evaluate and balance the mechanics of mcMMO in every update. ## About the Team @@ -31,6 +31,8 @@ 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) +[![EasyMFnE](https://www.gravatar.com/avatar/99c9a1fa3bbf957791ceac7b45daadb0.png)] +(https://github.com/EasyMFnE) ## Compiling diff --git a/src/main/java/com/gmail/nossr50/commands/skills/AlchemyCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/AlchemyCommand.java new file mode 100644 index 000000000..c9ed5118e --- /dev/null +++ b/src/main/java/com/gmail/nossr50/commands/skills/AlchemyCommand.java @@ -0,0 +1,99 @@ +package com.gmail.nossr50.commands.skills; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.entity.Player; + +import com.gmail.nossr50.config.AdvancedConfig; +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.util.Permissions; +import com.gmail.nossr50.util.player.UserManager; + +public class AlchemyCommand extends SkillCommand { + private String brewSpeed; + private String brewSpeedLucky; + + private int tier; + private int ingredientCount; + private String ingredientList; + + private boolean canCatalysis; + private boolean canConcoctions; + + public AlchemyCommand() { + super(SkillType.ALCHEMY); + } + + protected String[] calculateAbilityDisplayValues(Player player, float skillValue, boolean isLucky) { + 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; + + return displayValues; + } + + @Override + protected void dataCalculations(Player player, float skillValue, boolean isLucky) { + // CATALYSIS + if (canCatalysis) { + String[] catalysisStrings = calculateAbilityDisplayValues(player, skillValue, 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(); + } + } + + @Override + protected void permissionsCheck(Player player) { + canCatalysis = Permissions.catalysis(player); + canConcoctions = Permissions.concoctions(player); + } + + @Override + protected List effectsDisplay() { + List messages = new ArrayList(); + + if (canCatalysis) { + messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Alchemy.Effect.0"), LocaleLoader.getString("Alchemy.Effect.1"))); + } + + if (canConcoctions) { + messages.add(LocaleLoader.getString("Effects.Template", LocaleLoader.getString("Alchemy.Effect.2"), LocaleLoader.getString("Alchemy.Effect.3"))); + } + + return messages; + } + + @Override + protected List statsDisplay(Player player, float skillValue, boolean hasEndurance, boolean isLucky) { + List messages = new ArrayList(); + + if (canCatalysis) { + int unlockLevel = AdvancedConfig.getInstance().getCatalysisUnlockLevel(); + + if (skillValue < unlockLevel) { + messages.add(LocaleLoader.getString("Ability.Generic.Template.Lock", LocaleLoader.getString("Alchemy.Ability.Locked.0", unlockLevel))); + } + else { + messages.add(LocaleLoader.getString("Alchemy.Catalysis.Speed", brewSpeed) + (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus", brewSpeedLucky) : "")); + } + } + + if (canConcoctions) { + messages.add(LocaleLoader.getString("Alchemy.Concoctions.Rank", tier, Tier.values().length)); + messages.add(LocaleLoader.getString("Alchemy.Concoctions.Ingredients", ingredientCount, ingredientList)); + } + + return messages; + } +} diff --git a/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java b/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java index bd4c808a8..a251c5c59 100644 --- a/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java +++ b/src/main/java/com/gmail/nossr50/config/AdvancedConfig.java @@ -6,6 +6,7 @@ import java.util.List; import com.gmail.nossr50.datatypes.skills.SecondaryAbility; import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.skills.alchemy.Alchemy; import com.gmail.nossr50.skills.fishing.Fishing; import com.gmail.nossr50.skills.mining.BlastMining; import com.gmail.nossr50.skills.repair.ArcaneForging; @@ -79,6 +80,38 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { reason.add("Skills.Acrobatics.GracefulRoll.DamageThreshold should be at least 0!"); } + /* ALCHEMY */ + if (getCatalysisUnlockLevel() < 0) { + reason.add("Skills.Alchemy.Catalysis.UnlockLevel should be at least 0!"); + } + + if (getCatalysisMaxBonusLevel() <= getCatalysisUnlockLevel()) { + reason.add("Skills.Alchemy.Catalysis.MaxBonusLevel should be greater than Skills.Alchemy.Catalysis.UnlockLevel!"); + } + + if (getCatalysisMinSpeed() <= 0) { + reason.add("Skills.Alchemy.Catalysis.MinSpeed must be greater than 0!"); + } + + if (getCatalysisMaxSpeed() < getCatalysisMinSpeed()) { + reason.add("Skills.Alchemy.Catalysis.MaxSpeed should be at least Skills.Alchemy.Catalysis.MinSpeed!"); + } + + List alchemyTierList = Arrays.asList(Alchemy.Tier.values()); + for (Alchemy.Tier tier : alchemyTierList) { + if (getConcoctionsTierLevel(tier) < 0) { + reason.add("Skills.Alchemy.Rank_Levels.Rank_" + tier.toNumerical() + " should be at least 0!"); + } + + if (tier != Alchemy.Tier.fromNumerical(Alchemy.Tier.values().length)) { + Alchemy.Tier nextTier = alchemyTierList.get(alchemyTierList.indexOf(tier) - 1); + + if (getConcoctionsTierLevel(tier) >= getConcoctionsTierLevel(nextTier)) { + reason.add("Skills.Alchemy.Rank_Levels.Rank_" + tier.toNumerical() + " should be less than Skills.Alchemy.Rank_Levels.Rank_" + nextTier.toNumerical() + "!"); + } + } + } + /* ARCHERY */ if (getSkillShotIncreaseLevel() < 1) { reason.add("Skills.Archery.SkillShot.IncreaseLevel should be at least 1!"); @@ -624,6 +657,14 @@ public class AdvancedConfig extends AutoUpdateConfigLoader { public double getGracefulRollDamageThreshold() { return config.getDouble("Skills.Acrobatics.GracefulRoll.DamageThreshold", 14.0D); } + /* ALCHEMY */ + public int getCatalysisUnlockLevel() { return config.getInt("Skills.Alchemy.Catalysis.UnlockLevel", 100); } + public int getCatalysisMaxBonusLevel() { return config.getInt("Skills.Alchemy.Catalysis.MaxBonusLevel", 1000); } + public double getCatalysisMinSpeed() { return config.getDouble("Skills.Alchemy.Catalysis.MinSpeed", 1.0D); } + public double getCatalysisMaxSpeed() { return config.getDouble("Skills.Alchemy.Catalysis.MaxSpeed", 4.0D); } + + public int getConcoctionsTierLevel(Alchemy.Tier tier) { return config.getInt("Skills.Alchemy.Rank_Levels.Rank_" + tier.toNumerical()); } + /* ARCHERY */ public int getSkillShotIncreaseLevel() { return config.getInt("Skills.Archery.SkillShot.IncreaseLevel", 50); } public double getSkillShotIncreasePercentage() { return config.getDouble("Skills.Archery.SkillShot.IncreasePercentage", 0.1D); } diff --git a/src/main/java/com/gmail/nossr50/config/Config.java b/src/main/java/com/gmail/nossr50/config/Config.java index d6c1ca374..1c29b3a9c 100644 --- a/src/main/java/com/gmail/nossr50/config/Config.java +++ b/src/main/java/com/gmail/nossr50/config/Config.java @@ -403,6 +403,10 @@ public class Config extends AutoUpdateConfigLoader { public boolean getDodgeLightningDisabled() { return config.getBoolean("Skills.Acrobatics.Prevent_Dodge_Lightning", false); } public boolean getPreventXPAfterTeleport() { return config.getBoolean("Skills.Acrobatics.Prevent_XP_After_Teleport", true); } + /* Alchemy */ + public boolean getEnabledForHoppers() { return config.getBoolean("Skills.Alchemy.Enabled_for_Hoppers", true); } + public boolean getPreventHopperTransfer() { return config.getBoolean("Skills.Alchemy.Prevent_Hopper_Transfer", false); } + /* Fishing */ public boolean getFishingDropsEnabled() { return config.getBoolean("Skills.Fishing.Drops_Enabled", true); } diff --git a/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java b/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java index bcdc12474..d5fa3d9fd 100644 --- a/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java +++ b/src/main/java/com/gmail/nossr50/config/experience/ExperienceConfig.java @@ -76,6 +76,11 @@ public class ExperienceConfig extends AutoUpdateConfigLoader { * XP SETTINGS */ + /* Alchemy */ + if (getPotionXP() <= 0) { + reason.add("Experience.Alchemy.Potion should be greater than 0!"); + } + /* Combat XP Multipliers */ if (getAnimalsXP() < 0) { reason.add("Experience.Combat.Multiplier.Animals should be at least 0!"); @@ -199,6 +204,9 @@ public class ExperienceConfig extends AutoUpdateConfigLoader { public double getFeatherFallXPModifier() { return config.getDouble("Experience.Acrobatics.FeatherFall_Multiplier", 2.0); } + /* Alchemy */ + public double getPotionXP() { return config.getDouble("Experience.Alchemy.Potion", 150D); } + /* Fishing */ public int getFishingBaseXP() { return config.getInt("Experience.Fishing.Base", 800); } public int getFishingShakeXP() { return config.getInt("Experience.Fishing.Shake", 50); } diff --git a/src/main/java/com/gmail/nossr50/config/potion/PotionConfig.java b/src/main/java/com/gmail/nossr50/config/potion/PotionConfig.java new file mode 100644 index 000000000..3a12d6790 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/config/potion/PotionConfig.java @@ -0,0 +1,188 @@ +package com.gmail.nossr50.config.potion; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import com.gmail.nossr50.mcMMO; +import com.gmail.nossr50.config.ConfigLoader; +import com.gmail.nossr50.datatypes.AlchemyPotion; + +public class PotionConfig extends ConfigLoader { + private static PotionConfig instance; + + public List concoctionsIngredientsTierOne = new ArrayList(); + public List concoctionsIngredientsTierTwo = new ArrayList(); + public List concoctionsIngredientsTierThree = new ArrayList(); + public List concoctionsIngredientsTierFour = new ArrayList(); + public List concoctionsIngredientsTierFive = new ArrayList(); + public List concoctionsIngredientsTierSix = new ArrayList(); + public List concoctionsIngredientsTierSeven = new ArrayList(); + public List concoctionsIngredientsTierEight = new ArrayList(); + + public Map potionMap = new HashMap(); + + private PotionConfig() { + super("potions.yml"); + loadKeys(); + } + + public static PotionConfig getInstance() { + if (instance == null) { + instance = new PotionConfig(); + } + + return instance; + } + + @Override + protected void loadKeys() { + loadConcoctions(); + loadPotionMap(); + } + + private void loadConcoctions() { + ConfigurationSection concoctionSection = config.getConfigurationSection("Concoctions"); + + loadConcoctionsTier(concoctionsIngredientsTierOne, concoctionSection.getStringList("Tier_One_Ingredients")); + loadConcoctionsTier(concoctionsIngredientsTierTwo, concoctionSection.getStringList("Tier_Two_Ingredients")); + loadConcoctionsTier(concoctionsIngredientsTierThree, concoctionSection.getStringList("Tier_Three_Ingredients")); + loadConcoctionsTier(concoctionsIngredientsTierFour, concoctionSection.getStringList("Tier_Four_Ingredients")); + loadConcoctionsTier(concoctionsIngredientsTierFive, concoctionSection.getStringList("Tier_Five_Ingredients")); + 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); + concoctionsIngredientsTierFive.addAll(concoctionsIngredientsTierFour); + concoctionsIngredientsTierSix.addAll(concoctionsIngredientsTierFive); + concoctionsIngredientsTierSeven.addAll(concoctionsIngredientsTierSix); + concoctionsIngredientsTierEight.addAll(concoctionsIngredientsTierSeven); + } + + private void loadConcoctionsTier(List ingredientList, List ingredientStrings) { + if (ingredientStrings != null && ingredientStrings.size() > 0) { + for (String ingredientString : ingredientStrings) { + ItemStack ingredient = loadIngredient(ingredientString); + + if (ingredient != null) { + ingredientList.add(ingredient); + } + } + } + } + + /** + * Find the Potions configuration section and load all defined potions. + */ + private void loadPotionMap() { + ConfigurationSection potionSection = config.getConfigurationSection("Potions"); + int pass = 0; + int fail = 0; + + for (String dataValue : potionSection.getKeys(false)) { + AlchemyPotion potion = loadPotion(potionSection.getConfigurationSection(dataValue)); + + if (potion != null) { + potionMap.put(potion.getDataValue(), potion); + pass++; + } + else { + fail++; + } + } + + mcMMO.p.getLogger().info("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 lore = new ArrayList(); + if (potion_section.contains("Lore")) { + for (String line : potion_section.getStringList("Lore")) { + lore.add(line); + } + } + + List effects = new ArrayList(); + if (potion_section.contains("Effects")) { + for (String effect : potion_section.getStringList("Effects")) { + String[] parts = effect.split(" "); + + PotionEffectType type = parts.length > 0 ? PotionEffectType.getByName(parts[0]) : null; + int amplifier = parts.length > 1 ? Integer.parseInt(parts[1]) : 0; + int duration = parts.length > 2 ? Integer.parseInt(parts[2]) : 0; + + if (type != null) { + effects.add(new PotionEffect(type, duration, amplifier)); + } + else { + mcMMO.p.getLogger().warning("Failed to parse effect for potion " + name + ": " + effect); + } + } + } + + Map children = new HashMap(); + if (potion_section.contains("Children")) { + for (String child : potion_section.getConfigurationSection("Children").getKeys(false)) { + ItemStack ingredient = loadIngredient(child); + if (ingredient != null) { + children.put(ingredient, Short.parseShort(potion_section.getConfigurationSection("Children").getString(child))); + } + else { + mcMMO.p.getLogger().warning("Failed to parse child for potion " + name + ": " + child); + } + } + } + + return new AlchemyPotion(dataValue, name, lore, effects, children); + } + catch (Exception e) { + mcMMO.p.getLogger().warning("Failed to load Alchemy potion: " + potion_section.getName()); + return null; + } + } + + /** + * Parse a string representation of an ingredient. + * Format: '<MATERIAL>[: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; + } +} diff --git a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java index 0484a394e..488e664e5 100644 --- a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java @@ -265,6 +265,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager { writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":"); MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":"); + writer.append(profile.getSkillLevel(SkillType.ALCHEMY)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.ALCHEMY)).append(":"); writer.append("\r\n"); } } @@ -354,7 +356,9 @@ public final class FlatfileDatabaseManager implements DatabaseManager { out.append("0:"); // Blast Mining out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin out.append(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD - + out.append("0:"); // Alchemy + out.append("0:"); // AlchemyXp + // Add more in the same format as the line above out.newLine(); @@ -503,6 +507,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { List unarmed = new ArrayList(); List taming = new ArrayList(); List fishing = new ArrayList(); + List alchemy = new ArrayList(); BufferedReader in = null; String playerName = null; @@ -520,6 +525,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { Map skills = getSkillMapFromLine(data); powerLevel += putStat(acrobatics, playerName, skills.get(SkillType.ACROBATICS)); + powerLevel += putStat(alchemy, playerName, skills.get(SkillType.ALCHEMY)); powerLevel += putStat(archery, playerName, skills.get(SkillType.ARCHERY)); powerLevel += putStat(axes, playerName, skills.get(SkillType.AXES)); powerLevel += putStat(excavation, playerName, skills.get(SkillType.EXCAVATION)); @@ -557,6 +563,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { Collections.sort(acrobatics, c); Collections.sort(taming, c); Collections.sort(fishing, c); + Collections.sort(alchemy, c); Collections.sort(powerLevels, c); playerStatHash.put(SkillType.MINING, mining); @@ -571,6 +578,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { playerStatHash.put(SkillType.ACROBATICS, acrobatics); playerStatHash.put(SkillType.TAMING, taming); playerStatHash.put(SkillType.FISHING, fishing); + playerStatHash.put(SkillType.ALCHEMY, alchemy); } /** @@ -623,7 +631,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { } // If they're valid, rewrite them to the file. - if (character.length > 38) { + if (character.length > 40) { writer.append(line).append("\r\n"); continue; } @@ -675,6 +683,15 @@ public final class FlatfileDatabaseManager implements DatabaseManager { oldVersion = "1.4.06"; } } + if (character.length <= 40) { + // Addition of Alchemy + // Version 1.4.08 + newLine.append("0").append(":"); + newLine.append("0").append(":"); + if (oldVersion == null) { + oldVersion = "1.4.08"; + } + } if (oldVersion != null) { mcMMO.p.debug("Updating database line for player " + character[0] + " from before version " + oldVersion); @@ -771,6 +788,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { skillsXp.put(SkillType.AXES, (float) Integer.valueOf(character[21])); skillsXp.put(SkillType.ACROBATICS, (float) Integer.valueOf(character[22])); skillsXp.put(SkillType.FISHING, (float) Integer.valueOf(character[35])); + skillsXp.put(SkillType.ALCHEMY, (float) Integer.valueOf(character[40])); // Taming - Unused skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(character[32])); @@ -810,6 +828,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { skills.put(SkillType.AXES, Integer.valueOf(character[13])); skills.put(SkillType.ACROBATICS, Integer.valueOf(character[14])); skills.put(SkillType.FISHING, Integer.valueOf(character[34])); + skills.put(SkillType.ALCHEMY, Integer.valueOf(character[39])); return skills; } diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java index f7683616c..ac2f58ff7 100644 --- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java @@ -152,7 +152,7 @@ public final class SQLDatabaseManager implements DatabaseManager { + " taming = ?, mining = ?, repair = ?, woodcutting = ?" + ", unarmed = ?, herbalism = ?, excavation = ?" + ", archery = ?, swords = ?, axes = ?, acrobatics = ?" - + ", fishing = ? WHERE user_id = ?", + + ", fishing = ?, alchemy = ? WHERE user_id = ?", profile.getSkillLevel(SkillType.TAMING), profile.getSkillLevel(SkillType.MINING), profile.getSkillLevel(SkillType.REPAIR), @@ -165,13 +165,14 @@ public final class SQLDatabaseManager implements DatabaseManager { profile.getSkillLevel(SkillType.AXES), profile.getSkillLevel(SkillType.ACROBATICS), profile.getSkillLevel(SkillType.FISHING), + profile.getSkillLevel(SkillType.ALCHEMY), userId); success &= saveIntegers( "UPDATE " + tablePrefix + "experience SET " + " taming = ?, mining = ?, repair = ?, woodcutting = ?" + ", unarmed = ?, herbalism = ?, excavation = ?" + ", archery = ?, swords = ?, axes = ?, acrobatics = ?" - + ", fishing = ? WHERE user_id = ?", + + ", fishing = ?, alchemy = ? WHERE user_id = ?", profile.getSkillXpLevel(SkillType.TAMING), profile.getSkillXpLevel(SkillType.MINING), profile.getSkillXpLevel(SkillType.REPAIR), @@ -184,6 +185,7 @@ public final class SQLDatabaseManager implements DatabaseManager { profile.getSkillXpLevel(SkillType.AXES), profile.getSkillXpLevel(SkillType.ACROBATICS), profile.getSkillXpLevel(SkillType.FISHING), + profile.getSkillXpLevel(SkillType.ALCHEMY), userId); return success; } @@ -192,7 +194,7 @@ public final class SQLDatabaseManager implements DatabaseManager { List stats = new ArrayList(); if (checkConnected()) { - String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing" : skill.name().toLowerCase(); + String query = skill == null ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy" : skill.name().toLowerCase(); ResultSet resultSet = null; PreparedStatement statement = null; @@ -271,9 +273,9 @@ public final class SQLDatabaseManager implements DatabaseManager { } String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " + - "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > " + - "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " + + "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + + "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > " + + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)"; PreparedStatement statement = connection.prepareStatement(sql); @@ -286,11 +288,11 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.close(); - sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " + + sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " + - "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " + - "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing = " + - "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " + + "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy > 0 " + + "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy = " + + "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing+alchemy " + "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user"; statement = connection.prepareStatement(sql); @@ -359,8 +361,8 @@ public final class SQLDatabaseManager implements DatabaseManager { try { statement = connection.prepareStatement( "SELECT " - + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, " - + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, " + + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " + + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, " + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, " + "h.mobhealthbar " + "FROM " + tablePrefix + "users u " @@ -435,8 +437,8 @@ public final class SQLDatabaseManager implements DatabaseManager { try { statement = connection.prepareStatement( "SELECT " - + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, " - + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, " + + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, s.alchemy, " + + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, e.alchemy, " + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, " + "h.mobhealthbar " + "FROM " + tablePrefix + "users u " @@ -687,6 +689,7 @@ public final class SQLDatabaseManager implements DatabaseManager { + "`axes` int(10) unsigned NOT NULL DEFAULT '0'," + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0'," + "`fishing` int(10) unsigned NOT NULL DEFAULT '0'," + + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0'," + "PRIMARY KEY (`user_id`)) " + "DEFAULT CHARSET=latin1;"); write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "experience` (" @@ -703,6 +706,7 @@ public final class SQLDatabaseManager implements DatabaseManager { + "`axes` int(10) unsigned NOT NULL DEFAULT '0'," + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0'," + "`fishing` int(10) unsigned NOT NULL DEFAULT '0'," + + "`alchemy` int(10) unsigned NOT NULL DEFAULT '0'," + "PRIMARY KEY (`user_id`)) " + "DEFAULT CHARSET=latin1;"); @@ -728,6 +732,10 @@ public final class SQLDatabaseManager implements DatabaseManager { sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`fishing` ASC LIMIT 0 , 30"; break; + case ALCHEMY: + sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`alchemy` ASC LIMIT 0 , 30"; + break; + case INDEX: if (read("SHOW INDEX FROM " + tablePrefix + "skills").size() != 13 && checkConnected()) { mcMMO.p.getLogger().info("Indexing tables, this may take a while on larger databases"); @@ -830,6 +838,12 @@ public final class SQLDatabaseManager implements DatabaseManager { write("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "' ;"); break; + case ALCHEMY: + mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy..."); + write("ALTER TABLE `"+tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;"); + write("ALTER TABLE `"+tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;"); + break; + default: break; } @@ -1199,6 +1213,7 @@ public final class SQLDatabaseManager implements DatabaseManager { skills.put(SkillType.AXES, result.getInt(OFFSET_SKILLS + 10)); skills.put(SkillType.ACROBATICS, result.getInt(OFFSET_SKILLS + 11)); skills.put(SkillType.FISHING, result.getInt(OFFSET_SKILLS + 12)); + skills.put(SkillType.ALCHEMY, result.getInt(OFFSET_SKILLS + 13)); skillsXp.put(SkillType.TAMING, result.getFloat(OFFSET_XP + 1)); skillsXp.put(SkillType.MINING, result.getFloat(OFFSET_XP + 2)); @@ -1212,6 +1227,7 @@ public final class SQLDatabaseManager implements DatabaseManager { skillsXp.put(SkillType.AXES, result.getFloat(OFFSET_XP + 10)); skillsXp.put(SkillType.ACROBATICS, result.getFloat(OFFSET_XP + 11)); skillsXp.put(SkillType.FISHING, result.getFloat(OFFSET_XP + 12)); + skillsXp.put(SkillType.ALCHEMY, result.getFloat(OFFSET_XP + 13)); // Taming - Unused - result.getInt(OFFSET_DATS + 1) skillsDATS.put(AbilityType.SUPER_BREAKER, result.getInt(OFFSET_DATS + 2)); diff --git a/src/main/java/com/gmail/nossr50/datatypes/AlchemyPotion.java b/src/main/java/com/gmail/nossr50/datatypes/AlchemyPotion.java new file mode 100644 index 000000000..1a9dc2e55 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/datatypes/AlchemyPotion.java @@ -0,0 +1,101 @@ +package com.gmail.nossr50.datatypes; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; +import org.bukkit.potion.PotionEffect; + +public class AlchemyPotion { + private short dataValue; + private String name; + private List lore; + private List effects; + private Map children; + + public AlchemyPotion(short dataValue, String name, List lore, List effects, Map children) { + this.dataValue = dataValue; + this.lore = lore; + this.name = name; + this.effects = effects; + this.children = children; + } + + public String toString() { + 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(); + meta.setDisplayName(this.getName()); + if (this.getLore() != null && !this.getLore().isEmpty()) { + meta.setLore(this.getLore()); + } + if (!this.getEffects().isEmpty()) { + for (PotionEffect effect : this.getEffects()) { + meta.addCustomEffect(effect, true); + } + } + potion.setItemMeta(meta); + return potion; + } + + public short getDataValue() { + return dataValue; + } + + public void setDataValue(short data_value) { + this.dataValue = data_value; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List getLore() { + return lore; + } + + public void setLore(List lore) { + this.lore = lore; + } + + public List getEffects() { + return effects; + } + + public void setEffects(List effects) { + this.effects = effects; + } + + public Map getChildren() { + return children; + } + + public void setChildren(Map children) { + this.children = children; + } + + public Short getChildDataValue(ItemStack ingredient) { + if (!children.isEmpty()) { + for (Entry child : children.entrySet()) { + if (ingredient.isSimilar(child.getKey())) { + return child.getValue(); + } + } + } + return -1; + } +} diff --git a/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java b/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java index 2d2d2133c..2223c1969 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java @@ -7,6 +7,7 @@ public enum DatabaseUpdateType { MOB_HEALTHBARS, PARTY_NAMES, KILL_ORPHANS, - DROPPED_SPOUT + DROPPED_SPOUT, + ALCHEMY ; } diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java index 8740ceea9..8cfdba6df 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java @@ -31,6 +31,7 @@ import com.gmail.nossr50.runnables.skills.AbilityDisableTask; import com.gmail.nossr50.runnables.skills.ToolLowerTask; import com.gmail.nossr50.skills.SkillManager; import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager; +import com.gmail.nossr50.skills.alchemy.AlchemyManager; import com.gmail.nossr50.skills.archery.ArcheryManager; import com.gmail.nossr50.skills.axes.AxesManager; import com.gmail.nossr50.skills.child.FamilyTree; @@ -192,6 +193,10 @@ public class McMMOPlayer { return (AcrobaticsManager) skillManagers.get(SkillType.ACROBATICS); } + public AlchemyManager getAlchemyManager() { + return (AlchemyManager) skillManagers.get(SkillType.ALCHEMY); + } + public ArcheryManager getArcheryManager() { return (ArcheryManager) skillManagers.get(SkillType.ARCHERY); } diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java index 76e861c03..279b80851 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/SkillType.java @@ -15,6 +15,7 @@ import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.skills.SkillManager; import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager; +import com.gmail.nossr50.skills.alchemy.AlchemyManager; import com.gmail.nossr50.skills.archery.ArcheryManager; import com.gmail.nossr50.skills.axes.AxesManager; import com.gmail.nossr50.skills.excavation.ExcavationManager; @@ -35,6 +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), 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)), @@ -61,7 +63,7 @@ public enum SkillType { public static final List COMBAT_SKILLS = ImmutableList.of(ARCHERY, AXES, SWORDS, TAMING, UNARMED); public static final List GATHERING_SKILLS = ImmutableList.of(EXCAVATION, FISHING, HERBALISM, MINING, WOODCUTTING); - public static final List MISC_SKILLS = ImmutableList.of(ACROBATICS, REPAIR, SMELTING); + public static final List MISC_SKILLS = ImmutableList.of(ACROBATICS, ALCHEMY, REPAIR, SMELTING); static { List childSkills = new ArrayList(); diff --git a/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerBrewEvent.java b/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerBrewEvent.java new file mode 100644 index 000000000..e8c890e24 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerBrewEvent.java @@ -0,0 +1,37 @@ +package com.gmail.nossr50.events.skills.alchemy; + +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; + +import com.gmail.nossr50.datatypes.skills.SkillType; +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) { + super(player, SkillType.ALCHEMY); + this.brewingStand = brewingStand; + cancelled = false; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean newValue) { + this.cancelled = newValue; + } + + public Block getBrewingStandBlock() { + return brewingStand; + } + + public BrewingStand getBrewingStand() { + return brewingStand.getState() instanceof BrewingStand ? (BrewingStand) brewingStand.getState() : null; + } +} diff --git a/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerCatalysisEvent.java b/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerCatalysisEvent.java new file mode 100644 index 000000000..c7872baa8 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/events/skills/alchemy/McMMOPlayerCatalysisEvent.java @@ -0,0 +1,35 @@ +package com.gmail.nossr50.events.skills.alchemy; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; + +import com.gmail.nossr50.datatypes.skills.SkillType; +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) { + super(player, SkillType.ALCHEMY); + this.speed = speed; + cancelled = false; + } + + public boolean isCancelled() { + return cancelled; + } + + public void setCancelled(boolean newValue) { + this.cancelled = newValue; + } + + public double getSpeed() { + return speed; + } + + public void setSpeed(double speed) { + this.speed = speed; + } +} diff --git a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java index d900328bf..c5e75b122 100644 --- a/src/main/java/com/gmail/nossr50/listeners/BlockListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/BlockListener.java @@ -8,6 +8,7 @@ import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; +import org.bukkit.block.BrewingStand; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -30,6 +31,7 @@ import com.gmail.nossr50.datatypes.skills.ToolType; import com.gmail.nossr50.events.fake.FakeBlockBreakEvent; import com.gmail.nossr50.events.fake.FakeBlockDamageEvent; import com.gmail.nossr50.runnables.StickyPistonTrackerTask; +import com.gmail.nossr50.skills.alchemy.Alchemy; import com.gmail.nossr50.skills.excavation.ExcavationManager; import com.gmail.nossr50.skills.herbalism.HerbalismManager; import com.gmail.nossr50.skills.mining.MiningManager; @@ -135,6 +137,11 @@ public class BlockListener implements Listener { return; } + /* ALCHEMY - Cancel any brew in progress for that BrewingStand */ + if (blockState instanceof BrewingStand && Alchemy.brewingStandMap.containsKey(event.getBlock())) { + Alchemy.brewingStandMap.get(event.getBlock()).cancelBrew(); + } + Player player = event.getPlayer(); if (Misc.isNPCEntity(player) || player.getGameMode() == GameMode.CREATIVE) { diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index b8f18f4dc..2e6113173 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -28,9 +28,12 @@ import org.bukkit.event.entity.EntityTameEvent; import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.event.entity.ExplosionPrimeEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; +import org.bukkit.event.entity.PotionSplashEvent; import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.potion.PotionEffect; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.AdvancedConfig; @@ -592,4 +595,23 @@ public class EntityListener implements Listener { event.setCancelled(true); } } + + /** + * 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())); + } + } + return; + } + } } diff --git a/src/main/java/com/gmail/nossr50/listeners/InventoryListener.java b/src/main/java/com/gmail/nossr50/listeners/InventoryListener.java index 91f5707ce..24a1a010e 100644 --- a/src/main/java/com/gmail/nossr50/listeners/InventoryListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/InventoryListener.java @@ -2,8 +2,10 @@ package com.gmail.nossr50.listeners; import java.util.List; +import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; +import org.bukkit.block.BrewingStand; import org.bukkit.block.Furnace; import org.bukkit.entity.HumanEntity; import org.bukkit.entity.Player; @@ -16,16 +18,21 @@ import org.bukkit.event.inventory.FurnaceExtractEvent; import org.bukkit.event.inventory.FurnaceSmeltEvent; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; +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.FurnaceInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.metadata.MetadataValue; import com.gmail.nossr50.mcMMO; +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.AlchemyPotionBrewer; import com.gmail.nossr50.util.ItemUtils; import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Permissions; @@ -129,6 +136,48 @@ public class InventoryListener implements Listener { event.setExpToDrop(UserManager.getPlayer(player).getSmeltingManager().vanillaXPBoost(event.getExpToDrop())); } + @EventHandler(ignoreCancelled = true) + public void onAlchemyClickEvent(InventoryClickEvent event) { + if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) { + return; + } + + if (!(event.getWhoClicked() instanceof Player) || Misc.isNPCEntity(event.getWhoClicked()) || !Permissions.concoctions(event.getWhoClicked())) { + return; + } + + AlchemyPotionBrewer.handleInventoryClick(event); + } + + @EventHandler(ignoreCancelled = true) + public void onAlchemyDragEvent(InventoryDragEvent event) { + if (event.getInventory().getType() != InventoryType.BREWING || !(event.getInventory().getHolder() instanceof BrewingStand)) { + return; + } + + if (!(event.getWhoClicked() instanceof Player) || Misc.isNPCEntity(event.getWhoClicked()) || !Permissions.concoctions(event.getWhoClicked())) { + return; + } + + AlchemyPotionBrewer.handleInventoryDrag(event); + } + + @EventHandler(ignoreCancelled = true) + public void onAlchemyMoveItemEvent(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) { + event.setCancelled(true); + return; + } + + if (Config.getInstance().getEnabledForHoppers()) { + AlchemyPotionBrewer.handleInventoryMoveItem(event); + } + } + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onInventoryClickEvent(InventoryClickEvent event) { SkillUtils.removeAbilityBuff(event.getCurrentItem()); diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index bfd5666db..0b4daa327 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -19,6 +19,7 @@ import com.gmail.nossr50.config.mods.CustomBlockConfig; import com.gmail.nossr50.config.mods.CustomEntityConfig; import com.gmail.nossr50.config.mods.CustomToolConfig; import com.gmail.nossr50.config.repair.RepairConfigManager; +import com.gmail.nossr50.config.potion.PotionConfig; import com.gmail.nossr50.config.treasure.TreasureConfig; import com.gmail.nossr50.database.DatabaseManager; import com.gmail.nossr50.database.DatabaseManagerFactory; @@ -38,6 +39,7 @@ import com.gmail.nossr50.runnables.database.UserPurgeTask; import com.gmail.nossr50.runnables.party.PartyAutoKickTask; import com.gmail.nossr50.runnables.player.PowerLevelUpdatingTask; import com.gmail.nossr50.runnables.skills.BleedTimerTask; +import com.gmail.nossr50.skills.alchemy.Alchemy; import com.gmail.nossr50.skills.child.ChildConfig; import com.gmail.nossr50.skills.repair.repairables.Repairable; import com.gmail.nossr50.skills.repair.repairables.RepairableManager; @@ -198,6 +200,7 @@ public class mcMMO extends JavaPlugin { @Override public void onDisable() { try { + Alchemy.finishAllBrews(); // Finish all partially complete AlchemyBrewTasks to prevent vanilla brewing continuation on restart UserManager.saveAll(); // Make sure to save player information if the server shuts down PartyManager.saveParties(); // Save our parties ScoreboardManager.teardownAll(); @@ -358,6 +361,7 @@ public class mcMMO extends JavaPlugin { TreasureConfig.getInstance(); HiddenConfig.getInstance(); AdvancedConfig.getInstance(); + PotionConfig.getInstance(); new ChildConfig(); List repairables = new ArrayList(); diff --git a/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewCheckTask.java b/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewCheckTask.java new file mode 100644 index 000000000..7abcbd11e --- /dev/null +++ b/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewCheckTask.java @@ -0,0 +1,42 @@ +package com.gmail.nossr50.runnables.skills; + +import java.util.Arrays; + +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; + +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 Block brewingStand; + private ItemStack[] oldInventory; + private ItemStack[] newInventory; + + public AlchemyBrewCheckTask(Player player, BrewingStand brewingStand) { + this.player = player; + this.brewingStand = brewingStand.getBlock(); + this.oldInventory = Arrays.copyOfRange(brewingStand.getInventory().getContents(), 0, 4); + } + + @Override + public void run() { + this.newInventory = Arrays.copyOfRange(((BrewingStand) brewingStand.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)) { + Alchemy.brewingStandMap.get(brewingStand).cancelBrew(); + } + } + if (!Alchemy.brewingStandMap.containsKey(brewingStand) && AlchemyPotionBrewer.isValidBrew(player, newInventory)) { + Alchemy.brewingStandMap.put(brewingStand, new AlchemyBrewTask(brewingStand, player)); + } + } + +} diff --git a/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewTask.java b/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewTask.java new file mode 100644 index 000000000..ff77b334b --- /dev/null +++ b/src/main/java/com/gmail/nossr50/runnables/skills/AlchemyBrewTask.java @@ -0,0 +1,95 @@ +package com.gmail.nossr50.runnables.skills; + +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.BrewingStand; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitRunnable; + +import com.gmail.nossr50.mcMMO; +import com.gmail.nossr50.datatypes.skills.SkillType; +import com.gmail.nossr50.events.skills.alchemy.McMMOPlayerBrewEvent; +import com.gmail.nossr50.events.skills.alchemy.McMMOPlayerCatalysisEvent; +import com.gmail.nossr50.skills.alchemy.Alchemy; +import com.gmail.nossr50.skills.alchemy.AlchemyPotionBrewer; +import com.gmail.nossr50.util.Misc; +import com.gmail.nossr50.util.Permissions; +import com.gmail.nossr50.util.player.UserManager; + +public class AlchemyBrewTask extends BukkitRunnable { + private final double DEFAULT_BREW_SPEED = 1.0; + private final int DEFAULT_BREW_TICKS = 400; + + private Block brewingStand; + private double brewSpeed; + private double brewTimer; + private Player player; + + public AlchemyBrewTask(Block brewingStand, Player player) { + this.brewingStand = brewingStand; + this.player = player; + + brewSpeed = DEFAULT_BREW_SPEED; + brewTimer = DEFAULT_BREW_TICKS; + + if (player != null && !Misc.isNPCEntity(player) && Permissions.catalysis(player)) { + double catalysis = UserManager.getPlayer(player).getAlchemyManager().getBrewSpeed(); + + if (Permissions.lucky(player, SkillType.ALCHEMY)) { + catalysis = UserManager.getPlayer(player).getAlchemyManager().getBrewSpeedLucky(); + } + + McMMOPlayerCatalysisEvent event = new McMMOPlayerCatalysisEvent(player, catalysis); + Bukkit.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + brewSpeed = catalysis; + } + } + + if (Alchemy.brewingStandMap.containsKey(brewingStand)) { + Alchemy.brewingStandMap.get(brewingStand).cancel(); + } + + Alchemy.brewingStandMap.put(brewingStand, this); + this.runTaskTimer(mcMMO.p, 1, 1); + } + + @Override + public void run() { + brewTimer -= brewSpeed; + + // Vanilla potion brewing completes when BrewingTime == 1 + if (brewTimer < Math.max(brewSpeed, 2)) { + this.cancel(); + finish(); + } + else { + ((BrewingStand) brewingStand.getState()).setBrewingTime((int) brewTimer); + } + } + + private void finish() { + McMMOPlayerBrewEvent event = new McMMOPlayerBrewEvent(player, brewingStand); + Bukkit.getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + AlchemyPotionBrewer.finishBrewing(brewingStand, player, false); + } + + Alchemy.brewingStandMap.remove(brewingStand); + } + + public void finishImmediately() { + this.cancel(); + + AlchemyPotionBrewer.finishBrewing(brewingStand, player, true); + Alchemy.brewingStandMap.remove(brewingStand); + } + + public void cancelBrew() { + this.cancel(); + + ((BrewingStand) brewingStand.getState()).setBrewingTime(-1); + Alchemy.brewingStandMap.remove(brewingStand); + } +} diff --git a/src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java b/src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java new file mode 100644 index 000000000..676ec4e6b --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/alchemy/Alchemy.java @@ -0,0 +1,83 @@ +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 { + public enum Tier { + EIGHT(8), + SEVEN(7), + SIX(6), + FIVE(5), + FOUR(4), + THREE(3), + TWO(2), + ONE(1); + + int numerical; + + private Tier(int numerical) { + this.numerical = numerical; + } + + public int toNumerical() { + return numerical; + } + + public static Tier fromNumerical(int numerical) { + for (Tier tier : Tier.values()) { + if (tier.toNumerical() == numerical) { + return tier; + } + } + return null; + } + + protected int getLevel() { + 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 brewingStandMap = new HashMap(); + + 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.getLogger().info("Completing " + brewingStandMap.size() + " unfinished Alchemy brews."); + for (Entry entry : brewingStandMap.entrySet()) { + entry.getValue().finishImmediately(); + } + } +} diff --git a/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java b/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java new file mode 100644 index 000000000..abacfc923 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyManager.java @@ -0,0 +1,86 @@ +package com.gmail.nossr50.skills.alchemy; + +import java.util.List; + +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.SkillType; +import com.gmail.nossr50.skills.SkillManager; +import com.gmail.nossr50.util.Permissions; +import com.gmail.nossr50.util.StringUtils; + +public class AlchemyManager extends SkillManager { + private final double LUCKY_MODIFIER = 4.0 / 3.0; + + public AlchemyManager(McMMOPlayer mcMMOPlayer) { + super(mcMMOPlayer, SkillType.ALCHEMY); + } + + public boolean canCatalysis() { + return Permissions.catalysis(getPlayer()); + } + + 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()) { + return tier.toNumerical(); + } + } + + return 0; + } + + public List 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; + } + } + + public String getIngredientList() { + String list = ""; + for (ItemStack ingredient : getIngredients()) { + list += ", " + StringUtils.getPrettyItemString(ingredient.getType()) + (ingredient.getDurability() != 0 ? ":" + ingredient.getDurability() : ""); + } + return list.substring(2); + } + + 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)); + } + +} diff --git a/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyPotionBrewer.java b/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyPotionBrewer.java new file mode 100644 index 000000000..71654ddff --- /dev/null +++ b/src/main/java/com/gmail/nossr50/skills/alchemy/AlchemyPotionBrewer.java @@ -0,0 +1,349 @@ +package com.gmail.nossr50.skills.alchemy; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.block.Block; +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; +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.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 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; + } + } + } + } + + 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; + } + + private 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(); + + 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; + } + } + + private static boolean isValidIngredient(Player player, ItemStack item) { + if (isEmpty(item)) { + return false; + } + + for (ItemStack ingredient : getValidIngredients(player)) { + if (item.isSimilar(ingredient)) { + return true; + } + } + + return false; + } + + private static List getValidIngredients(Player player) { + if (player == null || !Permissions.concoctions(player)) { + return PotionConfig.getInstance().concoctionsIngredientsTierOne; + } + + switch (UserManager.getPlayer(player).getAlchemyManager().getTier()) { + 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; + } + } + + public static void finishBrewing(Block brewingStand, Player player, boolean forced) { + 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 (!forced) { + scheduleUpdate(inventory); + } + + return; + } + } + + 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; + } + else if (!isEmpty(to) && from.getAmount() >= from.getType().getMaxStackSize()) { + return false; + } + else if (isEmpty(to) || from.isSimilar(to)) { + if (isEmpty(to)) { + to = from.clone(); + to.setAmount(1); + } + 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; + } + + /** + * Transfer items between two ItemStacks, returning the leftover status + */ + private static boolean transferItems(InventoryView view, int fromSlot, int toSlot) { + if (isEmpty(view.getItem(fromSlot))) { + return false; + } + 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; + } + + private static void scheduleCheck(Player player, BrewingStand brewingStand) { + Bukkit.getScheduler().scheduleSyncDelayedTask(mcMMO.p, new AlchemyBrewCheckTask(player, brewingStand)); + } + + private static void scheduleUpdate(Inventory inventory) { + for (HumanEntity he : inventory.getViewers()) { + if (he instanceof Player) { + scheduleUpdate((Player) he); + } + } + } + private static void scheduleUpdate(Player player) { + Bukkit.getScheduler().scheduleSyncDelayedTask(mcMMO.p, new PlayerUpdateInventoryTask(player)); + } + + 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)) { + 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)) { + 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; + } + } +} diff --git a/src/main/java/com/gmail/nossr50/util/Permissions.java b/src/main/java/com/gmail/nossr50/util/Permissions.java index 699a2ef4d..af07341b6 100644 --- a/src/main/java/com/gmail/nossr50/util/Permissions.java +++ b/src/main/java/com/gmail/nossr50/util/Permissions.java @@ -126,6 +126,20 @@ public final class Permissions { public static boolean skillEnabled(Permissible permissible, SkillType skill) {return permissible.hasPermission("mcmmo.skills." + skill.toString().toLowerCase()); } public static boolean vanillaXpBoost(Permissible permissible, SkillType skill) { return permissible.hasPermission("mcmmo.ability." + skill.toString().toLowerCase() + ".vanillaxpboost"); } public static boolean secondaryAbilityEnabled(Permissible permissible, SecondaryAbility skillAbility) { return permissible.hasPermission("mcmmo.ability." + SkillType.bySecondaryAbility(skillAbility).toString().toLowerCase() + "." + StringUtils.getPrettySecondaryAbilityString(skillAbility).replace(" ", "").toLowerCase()); } + public static boolean bonusDamage(Permissible permissible, SkillType skill) { return permissible.hasPermission("mcmmo.ability." + skill.toString().toLowerCase() + ".bonusdamage"); } + + /* ACROBATICS */ + public static boolean dodge(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.dodge"); } + public static boolean gracefulRoll(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.gracefulroll"); } + public static boolean roll(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.acrobatics.roll"); } + + /* ALCHEMY */ + public static boolean catalysis(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.alchemy.catalysis"); } + public static boolean concoctions(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.alchemy.concoctions"); } + + /* ARCHERY */ + public static boolean arrowRetrieval(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.archery.trackarrows"); } + public static boolean daze(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.archery.daze"); } /* AXES */ public static boolean skullSplitter(Permissible permissible) { return permissible.hasPermission("mcmmo.ability.axes.skullsplitter"); } diff --git a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java index 44ba3c9f8..b9086b973 100644 --- a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java +++ b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java @@ -35,6 +35,7 @@ import com.gmail.nossr50.commands.player.McrankCommand; import com.gmail.nossr50.commands.player.McstatsCommand; import com.gmail.nossr50.commands.player.MctopCommand; import com.gmail.nossr50.commands.skills.AcrobaticsCommand; +import com.gmail.nossr50.commands.skills.AlchemyCommand; import com.gmail.nossr50.commands.skills.ArcheryCommand; import com.gmail.nossr50.commands.skills.AxesCommand; import com.gmail.nossr50.commands.skills.ExcavationCommand; @@ -76,6 +77,10 @@ public final class CommandRegistrationManager { command.setExecutor(new AcrobaticsCommand()); break; + case ALCHEMY: + command.setExecutor(new AlchemyCommand()); + break; + case ARCHERY: command.setExecutor(new ArcheryCommand()); break; diff --git a/src/main/resources/advanced.yml b/src/main/resources/advanced.yml index c7a71bd7e..f0e465691 100644 --- a/src/main/resources/advanced.yml +++ b/src/main/resources/advanced.yml @@ -48,6 +48,30 @@ Skills: MaxBonusLevel: 500 DamageThreshold: 14.0 # + # Settings for Alchemy + ### + Alchemy: + Catalysis: + # UnlockLevel: Alchemy level when the Catalysis ability unlocks + # MaxBonusLevel: Maximum bonus level of Catalysis, when a player reaches this level his brewing speed will be + # MinSpeed: Minimum brewing speed allowed when at or lower + # MaxSpeed: Maximum brewing speed allowed when at or higher + UnlockLevel: 100 + MaxBonusLevel: 1000 + MinSpeed: 1.0 + MaxSpeed: 4.0 + + # Rank_Levels: Alchemy level where rank gets unlocked + Rank_Levels: + Rank_1: 0 + Rank_2: 125 + Rank_3: 250 + Rank_4: 375 + Rank_5: 500 + Rank_6: 625 + Rank_7: 750 + Rank_8: 875 + # # Settings for Archery ### Archery: diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 5b1b7d497..0c1e47b24 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -251,6 +251,12 @@ Skills: Prevent_Dodge_Lightning: false Prevent_XP_After_Teleport: true Level_Cap: 0 + Alchemy: + # Allow Hoppers to transfer ingredients and brew Rank 1 Alchemy potions + Enabled_for_Hoppers: true + # Prevent Hoppers from transferring ingredients into Brewing Stands + Prevent_Hopper_Transfer: false + Level_Cap: 0 Archery: Enabled_For_PVP: true Enabled_For_PVE: true diff --git a/src/main/resources/experience.yml b/src/main/resources/experience.yml index c31646602..7a62a483c 100644 --- a/src/main/resources/experience.yml +++ b/src/main/resources/experience.yml @@ -52,6 +52,7 @@ Experience_Formula: Axes: 1.0 Repair: 1.0 Fishing: 1.0 + Alchemy: 1.0 # # Settings for XP conversion with '/mcconvert experience' @@ -73,6 +74,8 @@ Experience: # FeatherFall_Multiplier: Multiply Acrobatics XP by this value when wearing boots with the Feather Fall enchant FeatherFall_Multiplier: 2.0 + Alchemy: + Potion: 120 Fishing: Base: 800 Shake: 50 diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 20858d25e..66319ffe7 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -32,6 +32,19 @@ Acrobatics.Roll.Text=**Rolled** Acrobatics.SkillName=ACROBATICS Acrobatics.Skillup=[[YELLOW]]Acrobatics skill increased by {0}. Total ({1}) +#ALCHEMY +Alchemy.Effect.0=Catalysis +Alchemy.Effect.1=Increases potion brewing speed +Alchemy.Effect.2=Concoctions +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.Ingredients=[[RED]]Ingredients [[[YELLOW]]{0}[[RED]]]: [[YELLOW]]{1} +Alchemy.SkillName=ALCHEMY +Alchemy.Skillup=[[YELLOW]]Alchemy skill increased by {0}. Total ({1}) + #ARCHERY Archery.Combat.DazeChance=[[RED]]Chance to Daze: [[YELLOW]]{0} Archery.Combat.RetrieveChance=[[RED]]Chance to Retrieve Arrows: [[YELLOW]]{0} @@ -408,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),[[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),[[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, 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}! @@ -611,6 +624,7 @@ Party.ItemShare.Category.Misc=Misc ##xp Commands.XPGain.Acrobatics=Falling +Commands.XPGain.Alchemy=Brewing Potions Commands.XPGain.Archery=Attacking Monsters Commands.XPGain.Axes=Attacking Monsters Commands.XPGain.Child=Gains levels from Parent Skills diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 41e9dbf93..a43eece7c 100644 --- a/src/main/resources/plugin.yml +++ b/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] +authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking, EasyMFnE] website: http://dev.bukkit.org/server-mods/mcmmo/ main: com.gmail.nossr50.mcMMO @@ -89,6 +89,8 @@ commands: description: Detailed mcMMO skill info smelting: description: Detailed mcMMO skill info + alchemy: + description: Detailed mcMMO skill info adminchat: aliases: [ac, a] description: Toggle Admin chat or send admin chat messages @@ -139,6 +141,7 @@ permissions: description: Implies all mcmmo.ability permissions. children: mcmmo.ability.acrobatics.all: true + mcmmo.ability.alchemy.all: true mcmmo.ability.archery.all: true mcmmo.ability.axes.all: true mcmmo.ability.excavation.all: true @@ -168,6 +171,20 @@ permissions: description: Allows access to the Graceful Roll ability mcmmo.ability.acrobatics.roll: description: Allows access to the Roll ability + mcmmo.ability.alchemy.*: + default: false + description: Allows access to all Alchemy abilities + children: + mcmmo.ability.alchemy.all: true + mcmmo.ability.alchemy.all: + description: Allows access to all Alchemy abilities + children: + mcmmo.ability.alchemy.catalysis: true + mcmmo.ability.alchemy.concoctions: true + mcmmo.ability.alchemy.catalysis: + description: Allows access to the Catalysis ability + mcmmo.ability.alchemy.concoctions: + description: Allows access to the Concoctions ability mcmmo.ability.archery.*: default: false description: Allows access to all Archery abilities @@ -626,6 +643,7 @@ permissions: description: Implies all default mcmmo.commands permissions. children: mcmmo.commands.acrobatics: true + mcmmo.commands.alchemy: true mcmmo.commands.archery: true mcmmo.commands.axes: true mcmmo.commands.excavation: true @@ -695,6 +713,8 @@ permissions: description: Allows access to the addxp command mcmmo.commands.addxp.others: description: Allows access to the addxp command for other players + mcmmo.commands.alchemy: + description: Allows access to the alchemy command mcmmo.commands.archery: description: Allows access to the archery command mcmmo.commands.axes: @@ -833,6 +853,7 @@ permissions: children: mcmmo.commands.mctop: true mcmmo.commands.mctop.acrobatics: true + mcmmo.commands.mctop.alchemy: true mcmmo.commands.mctop.archery: true mcmmo.commands.mctop.axes: true mcmmo.commands.mctop.excavation: true @@ -849,6 +870,8 @@ permissions: description: Allows access to the mctop command mcmmo.commands.mctop.acrobatics: description: Allows access to the mctop command for acrobatics + mcmmo.commands.mctop.alchemy: + description: Allows access to the mctop command for alchemy mcmmo.commands.mctop.archery: description: Allows access to the mctop command for archery mcmmo.commands.mctop.axes: @@ -1001,6 +1024,7 @@ permissions: children: mcmmo.commands.skillreset: true mcmmo.commands.skillreset.acrobatics: true + mcmmo.commands.skillreset.alchemy: true mcmmo.commands.skillreset.archery: true mcmmo.commands.skillreset.axes: true mcmmo.commands.skillreset.excavation: true @@ -1018,6 +1042,8 @@ permissions: description: Allows access to the skillreset command mcmmo.commands.skillreset.acrobatics: description: Allows access to the skillreset command for acrobatics + mcmmo.commands.skillreset.alchemy: + description: Allows access to the skillreset command for alchemy mcmmo.commands.skillreset.archery: description: Allows access to the skillreset command for archery mcmmo.commands.skillreset.axes: @@ -1040,6 +1066,7 @@ permissions: children: mcmmo.commands.skillreset.others: true mcmmo.commands.skillreset.others.acrobatics: true + mcmmo.commands.skillreset.others.alchemy: true mcmmo.commands.skillreset.others.archery: true mcmmo.commands.skillreset.others.axes: true mcmmo.commands.skillreset.others.excavation: true @@ -1056,6 +1083,8 @@ permissions: description: Allows access to the skillreset command for other players mcmmo.commands.skillreset.others.acrobatics: description: Allows access to the skillreset command for acrobatics for other players + mcmmo.commands.skillreset.others.alchemy: + description: Allows access to the skillreset command for alchemy for other players mcmmo.commands.skillreset.others.archery: description: Allows access to the skillreset command for archery for other players mcmmo.commands.skillreset.others.axes: @@ -1246,6 +1275,7 @@ permissions: description: Gives all abilities & skills a 33.3% better chance to activate. children: mcmmo.perks.lucky.acrobatics: true + mcmmo.perks.lucky.alchemy: true mcmmo.perks.lucky.archery: true mcmmo.perks.lucky.axes: true mcmmo.perks.lucky.excavation: true @@ -1261,6 +1291,9 @@ permissions: mcmmo.perks.lucky.acrobatics: default: false description: Gives Acrobatics abilities & skills a 33.3% better chance to activate. + mcmmo.perks.lucky.alchemy: + default: false + description: Gives Alchemy abilities & skills a 33.3% better chance to activate. mcmmo.perks.lucky.archery: default: false description: Gives Archery abilities & skills a 33.3% better chance to activate. @@ -1326,6 +1359,7 @@ permissions: description: Mulitplies incoming XP by 2.5 children: mcmmo.perks.xp.150percentboost.acrobatics: true + mcmmo.perks.xp.150percentboost.alchemy: true mcmmo.perks.xp.150percentboost.archery: true mcmmo.perks.xp.150percentboost.axes: true mcmmo.perks.xp.150percentboost.excavation: true @@ -1341,6 +1375,9 @@ permissions: mcmmo.perks.xp.150percentboost.acrobatics: default: false description: Multiplies incoming Acrobatics XP by 2.5 + mcmmo.perks.xp.150percentboost.alchemy: + default: false + description: Multiplies incoming Alchemy XP by 2.5 mcmmo.perks.xp.150percentboost.archery: default: false description: Multiplies incoming Archery XP by 2.5 @@ -1392,6 +1429,7 @@ permissions: description: Mulitplies incoming XP by 1.5 children: mcmmo.perks.xp.50percentboost.acrobatics: true + mcmmo.perks.xp.50percentboost.alchemy: true mcmmo.perks.xp.50percentboost.archery: true mcmmo.perks.xp.50percentboost.axes: true mcmmo.perks.xp.50percentboost.excavation: true @@ -1407,6 +1445,9 @@ permissions: mcmmo.perks.xp.50percentboost.acrobatics: default: false description: Multiplies incoming Acrobatics XP by 1.5 + mcmmo.perks.xp.50percentboost.alchemy: + default: false + description: Multiplies incoming Acrobatics XP by 1.5 mcmmo.perks.xp.50percentboost.archery: default: false description: Multiplies incoming Archery XP by 1.5 @@ -1458,6 +1499,7 @@ permissions: description: Doubles incoming XP children: mcmmo.perks.xp.double.acrobatics: true + mcmmo.perks.xp.double.alchemy: true mcmmo.perks.xp.double.archery: true mcmmo.perks.xp.double.axes: true mcmmo.perks.xp.double.excavation: true @@ -1473,6 +1515,9 @@ permissions: mcmmo.perks.xp.double.acrobatics: default: false description: Doubles incoming Acrobatics XP + mcmmo.perks.xp.double.alchemy: + default: false + description: Doubles incoming Alchemy XP mcmmo.perks.xp.double.archery: default: false description: Doubles incoming Archery XP @@ -1524,6 +1569,7 @@ permissions: description: Quadruples incoming XP children: mcmmo.perks.xp.quadruple.acrobatics: true + mcmmo.perks.xp.quadruple.alchemy: true mcmmo.perks.xp.quadruple.archery: true mcmmo.perks.xp.quadruple.axes: true mcmmo.perks.xp.quadruple.excavation: true @@ -1539,6 +1585,9 @@ permissions: mcmmo.perks.xp.quadruple.acrobatics: default: false description: Quadruples incoming Acrobatics XP + mcmmo.perks.xp.quadruple.alchemy: + default: false + description: Quadruples incoming Alchemy XP mcmmo.perks.xp.quadruple.archery: default: false description: Quadruples incoming Archery XP @@ -1590,6 +1639,7 @@ permissions: description: Triples incoming XP children: mcmmo.perks.xp.triple.acrobatics: true + mcmmo.perks.xp.triple.alchemy: true mcmmo.perks.xp.triple.archery: true mcmmo.perks.xp.triple.axes: true mcmmo.perks.xp.triple.excavation: true @@ -1605,6 +1655,9 @@ permissions: mcmmo.perks.xp.triple.acrobatics: default: false description: Triples incoming Acrobatics XP + mcmmo.perks.xp.triple.alchemy: + default: false + description: Triples incoming Acrobatics XP mcmmo.perks.xp.triple.archery: default: false description: Triples incoming Archery XP @@ -1656,6 +1709,7 @@ permissions: description: Implies all mcmmo.skills permissions. children: mcmmo.skills.acrobatics: true + mcmmo.skills.alchemy: true mcmmo.skills.archery: true mcmmo.skills.axes: true mcmmo.skills.excavation: true @@ -1673,6 +1727,11 @@ permissions: children: mcmmo.ability.acrobatics.all: true mcmmo.commands.acrobatics: true + mcmmo.skills.alchemy: + description: Allows access to the Alchemy skill + children: + mcmmo.ability.alchemy.all: true + mcmmo.commands.alchemy: true mcmmo.skills.archery: description: Allows access to the Archery skill children: diff --git a/src/main/resources/potions.yml b/src/main/resources/potions.yml new file mode 100644 index 000000000..c99d49e26 --- /dev/null +++ b/src/main/resources/potions.yml @@ -0,0 +1,752 @@ +# +# Settings for Concoctions +# Last updated on ${project.version}-b${BUILD_NUMBER} +### +Concoctions: + Tier_One_Ingredients: + - BLAZE_POWDER + - FERMENTED_SPIDER_EYE + - GHAST_TEAR + - GLOWSTONE_DUST + - GOLDEN_CARROT + - MAGMA_CREAM + - NETHER_STALK + - REDSTONE + - SPECKLED_MELON + - SPIDER_EYE + - SUGAR + - SULPHUR + - WATER_LILY # TODO: 'RAW_FISH:3' (Minecraft 1.7) + Tier_Two_Ingredients: + - CARROT_ITEM + - SLIME_BALL + Tier_Three_Ingredients: + - QUARTZ + - RED_MUSHROOM + Tier_Four_Ingredients: + - APPLE + - ROTTEN_FLESH + Tier_Five_Ingredients: + - BROWN_MUSHROOM + - 'INK_SACK:0' + Tier_Six_Ingredients: + - 'LONG_GRASS:2' + Tier_Seven_Ingredients: + - POISONOUS_POTATO + Tier_Eight_Ingredients: + - 'GOLDEN_APPLE:0' + +# +# Settings for Potions +# +# : Data value used for identifying this potion +# Name: Custom display name for this potion (optional) +# Lore: ["lore1","lore2"...] Custom lore for this potion (section is optional) +# Children: The potential children of this potion (section is optional) +# : The ingredient used, and resultant potion's new data value +# Effects: List of strings for the potion's effects (section is optional) +# - " [EFFECT_TIER] [DURATION_TICKS]" +# +# DataValues - These potions follow the normal data value pattern except for bits 8-12: +# Bits 0-3: Liquid Color (http://minecraft.gamepedia.com/Data_values#Potions) +# Bit 4: (Unused) +# Bit 5: Power +# Bit 6: Extended +# Bit 7: (Unused) +# Bits 8-12: Custom Status Effect (http://minecraft.gamepedia.com/Data_values#Status_effects) +# Bit 13: Can become splash +# Bit 14: Splash +# Bit 15: (Unused) +### +Potions: + + ### NON-EFFECT POTIONS ##################################################### + + 0: # Water Bottle + Children: + BLAZE_POWDER: 8192 # Mundane Potion + FERMENTED_SPIDER_EYE: 4616 # Potion of Weakness + GHAST_TEAR: 8192 # Mundane Potion + GLOWSTONE_DUST: 32 # Thick Potion + MAGMA_CREAM: 8192 # Mundane Potion + NETHER_STALK: 16 # Awkward Potion + REDSTONE: 64 # Mundane Potion Extended + SPECKLED_MELON: 8192 # Mundane Potion + SPIDER_EYE: 8192 # Mundane Potion + SUGAR: 8192 # Mundane Potion + + 16: # Awkward Potion + Children: + APPLE: 5376 # Potion of Health Boost + BLAZE_POWDER: 1289 # Potion of Strength + BROWN_MUSHROOM: 2304 # Potion of Nausea + CARROT_ITEM: 768 # Potion of Haste + FERMENTED_SPIDER_EYE: 4616 # Potion of Weakness + GHAST_TEAR: 2561 # Potion of Regeneration + 'GOLDEN_APPLE:0': 2816 # Potion of Resistance + GOLDEN_CARROT: 4102 # Potion of Night Vision + 'INK_SACK:0': 3840 # Potion of Blindness + 'LONG_GRASS:2': 5888 # Potion of Saturation + MAGMA_CREAM: 3075 # Potion of Fire Resistance + POISONOUS_POTATO: 5120 # Potion of Decay + QUARTZ: 5632 # Potion of Absorption + RED_MUSHROOM: 2048 # Potion of Leaping + ROTTEN_FLESH: 4352 # Potion of Hunger + SLIME_BALL: 1024 # Potion of Dullness + SPECKLED_MELON: 1541 # Potion of Healing + 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) + + 32: # Thick Potion + Children: + FERMENTED_SPIDER_EYE: 4616 + + 64: # Mundane Potion Extended + Children: + FERMENTED_SPIDER_EYE: 4680 + + 8192: # Mundane Potion + Children: + FERMENTED_SPIDER_EYE: 4616 + SULPHUR: 16384 + + ### DRINKABLE POTIONS ###################################################### + + 258: # Potion of Swiftness + Effects: ["SPEED 0 3600"] + Children: + FERMENTED_SPIDER_EYE: 522 + GLOWSTONE_DUST: 290 + REDSTONE: 322 + SULPHUR: 16642 + 290: # Potion of Swiftness II + Effects: ["SPEED 1 1800"] + Children: + FERMENTED_SPIDER_EYE: 586 + REDSTONE: 322 + SULPHUR: 16642 + 322: # Potion of Swiftness Extended + Effects: ["SPEED 0 9600"] + Children: + FERMENTED_SPIDER_EYE: 522 + GLOWSTONE_DUST: 290 + SULPHUR: 16706 + + 522: # Potion of Slowness + Effects: ["SLOW 0 1800"] + Children: + REDSTONE: 586 + SULPHUR: 16906 + 586: # Potion of Slowness Extended + Effects: ["SLOW 0 4800"] + Children: + GLOWSTONE_DUST: 522 + SULPHUR: 16970 + + + 768: # Potion of Haste + Effects: ["FAST_DIGGING 0 3600"] + Children: + GLOWSTONE_DUST: 800 + REDSTONE: 832 + SULPHUR: 17152 + + 800: # Potion of Haste II + Effects: ["FAST_DIGGING 1 1800"] + Children: + REDSTONE: 832 + SULPHUR: 17184 + + 832: # Potion of Haste Extended + Effects: ["FAST_DIGGING 0 9600"] + Children: + GLOWSTONE_DUST: 800 + SULPHUR: 17216 + + 1024: # Potion of Dullness + Effects: ["SLOW_DIGGING 0 3600"] + Children: + GLOWSTONE_DUST: 1056 + REDSTONE: 1088 + SULPHUR: 17408 + 1056: # Potion of Dullness II + Effects: ["SLOW_DIGGING 1 1800"] + Children: + REDSTONE: 1088 + SULPHUR: 17408 + 1088: # Potion of Dullness Extended + Effects: ["SLOW_DIGGING 0 9600"] + Children: + GLOWSTONE_DUST: 1056 + SULPHUR: 17472 + + 1289: # Potion of Strength + Effects: ["INCREASE_DAMAGE 0 3600"] + Children: + FERMENTED_SPIDER_EYE: 4616 + GLOWSTONE_DUST: 1321 + REDSTONE: 1353 + SULPHUR: 17673 + 1321: # Potion of Strength II + Effects: ["INCREASE_DAMAGE 1 1800"] + Children: + FERMENTED_SPIDER_EYE: 4616 + REDSTONE: 1353 + SULPHUR: 17705 + 1353: # Potion of Strength Extended + Effects: ["INCREASE_DAMAGE 0 9600"] + Children: + FERMENTED_SPIDER_EYE: 4680 + GLOWSTONE_DUST: 1321 + SULPHUR: 17737 + + 1541: # Potion of Healing + Effects: ["HEAL 0"] + Children: + FERMENTED_SPIDER_EYE: 1804 + GLOWSTONE_DUST: 1573 + SULPHUR: 17925 + 1573: # Potion of Healing II + Effects: ["HEAL 1"] + Children: + FERMENTED_SPIDER_EYE: 1836 + REDSTONE: 1541 + SULPHUR: 17957 + + 1804: # Potion of Harming + Effects: ["HARM 0"] + Children: + GLOWSTONE_DUST: 1836 + SULPHUR: 18188 + 1836: # Potion of Harming II + Effects: ["HARM 1"] + Children: + REDSTONE: 1804 + SULPHUR: 18220 + + 2048: # Potion of Leaping + Effects: ["JUMP 0 3600"] + Children: + GLOWSTONE_DUST: 2080 + REDSTONE: 2112 + SULPHUR: 18432 + 2080: # Potion of Leaping II + Effects: ["JUMP 1 1800"] + Children: + REDSTONE: 2112 + SULPHUR: 18464 + 2112: # Potion of Leaping Extended + Effects: ["JUMP 0 9600"] + Children: + GLOWSTONE_DUST: 2080 + SULPHUR: 18496 + + 2304: # Potion of Nausea + Effects: ["CONFUSION 0 450"] + Children: + REDSTONE: 2368 + SULPHUR: 18688 + 2368: # Potion of Nausea Extended + Effects: ["CONFUSION 0 1200"] + Children: + GLOWSTONE_DUST: 2304 + SULPHUR: 18752 + + 2561: # Potion of Regeneration + Effects: ["REGENERATION 0 900"] + Children: + FERMENTED_SPIDER_EYE: 4616 + GLOWSTONE_DUST: 2593 + REDSTONE: 2625 + SULPHUR: 18945 + 2593: # Potion of Regeneration II + Effects: ["REGENERATION 1 450"] + Children: + FERMENTED_SPIDER_EYE: 4616 + REDSTONE: 2625 + SULPHUR: 18977 + 2625: # Potion of Regeneration Extended + Effects: ["REGENERATION 0 2400"] + Children: + FERMENTED_SPIDER_EYE: 4680 + GLOWSTONE_DUST: 2593 + SULPHUR: 19009 + + 2816: # Potion of Resistance + Effects: ["DAMAGE_RESISTANCE 0 450"] + Children: + GLOWSTONE_DUST: 2848 + REDSTONE: 2880 + SULPHUR: 19200 + 2848: # Potion of Resistance II + Effects: ["DAMAGE_RESISTANCE 1 225"] + Children: + REDSTONE: 2880 + SULPHUR: 19232 + 2880: # Potion of Resistance Extended + Effects: ["DAMAGE_RESISTANCE 0 1200"] + Children: + GLOWSTONE_DUST: 2848 + SULPHUR: 19264 + + 3075: # Potion of Fire Resistance + Effects: ["FIRE_RESISTANCE 0 3600"] + Children: + FERMENTED_SPIDER_EYE: 522 + REDSTONE: 3139 + SULPHUR: 19459 + 3139: # Potion of Fire Resistance Extended + Effects: ["FIRE_RESISTANCE 0 9600"] + Children: + FERMENTED_SPIDER_EYE: 586 + GLOWSTONE_DUST: 3075 + SULPHUR: 19523 + + 3341: # Potion of Water Breathing + Effects: ["WATER_BREATHING 0 3600"] + Children: + REDSTONE: 3405 + SULPHUR: 19725 + 3405: # Potion of Water Breathing Extended + Effects: ["WATER_BREATHING 0 9600"] + Children: + GLOWSTONE_DUST: 3341 + SULPHUR: 19789 + + 3598: # Potion of Invisibility + Effects: ["INVISIBILITY 0 3600"] + Children: + REDSTONE: 3662 + SULPHUR: 19982 + 3662: # Potion of Invisibility Extended + Effects: ["INVISIBILITY 0 9600"] + Children: + GLOWSTONE_DUST: 3598 + SULPHUR: 20046 + + 3840: # Potion of Blindness + Effects: ["BLINDNESS 0 225"] + Children: + REDSTONE: 3904 + SULPHUR: 20224 + 3904: # Potion of Blindness Extended + Effects: ["BLINDNESS 0 600"] + Children: + GLOWSTONE_DUST: 3840 + SULPHUR: 20288 + + 4102: # Potion of Night Vision + Effects: ["NIGHT_VISION 0 3600"] + Children: + FERMENTED_SPIDER_EYE: 3598 + REDSTONE: 4166 + SULPHUR: 20486 + 4166: # Potion of Night Vision Extended + Effects: ["NIGHT_VISION 0 9600"] + Children: + FERMENTED_SPIDER_EYE: 3662 + GLOWSTONE_DUST: 4102 + SULPHUR: 20550 + + 4352: # Potion of Hunger + Effects: ["HUNGER 0 900"] + Children: + GLOWSTONE_DUST: 4384 + REDSTONE: 4416 + SULPHUR: 20736 + 4284: # Potion of Hunger II + Effects: ["HUNGER 1 450"] + Children: + REDSTONE: 4416 + SULPHUR: 20768 + 4416: # Potion of Hunger Extended + Effects: ["HUNGER 0 2400"] + Children: + GLOWSTONE_DUST: 4384 + SULPHUR: 20800 + + 4616: # Potion of Weakness + Effects: ["WEAKNESS 0 1800"] + Children: + REDSTONE: 4680 + SULPHUR: 21000 + 4680: # Potion of Weakness Extended + Effects: ["WEAKNESS 0 4800"] + Children: + GLOWSTONE_DUST: 4616 + SULPHUR: 21064 + + 4868: # Potion of Poison + Effects: ["POISON 0 900"] + Children: + FERMENTED_SPIDER_EYE: 1804 + GLOWSTONE_DUST: 4900 + REDSTONE: 4932 + SULPHUR: 21252 + 4900: # Potion of Poison II + Effects: ["POISON 1 450"] + Children: + FERMENTED_SPIDER_EYE: 1836 + REDSTONE: 4932 + SULPHUR: 21284 + 4932: # Potion of Poison Extended + Effects: ["POISON 0 2400"] + Children: + FERMENTED_SPIDER_EYE: 1804 + GLOWSTONE_DUST: 4900 + SULPHUR: 21284 + + 5120: # Potion of Decay + Effects: ["WITHER 0 450"] + Children: + GLOWSTONE_DUST: 5152 + REDSTONE: 5184 + SULPHUR: 21504 + 5152: # Potion of Decay II + Effects: ["WITHER 1 225"] + Children: + REDSTONE: 5184 + SULPHUR: 21536 + 5184: # Potion of Decay Extended + Effects: ["WITHER 0 1200"] + Children: + GLOWSTONE_DUST: 5152 + SULPHUR: 21568 + + 5376: # Potion of Health Boost + Effects: ["HEALTH_BOOST 0 1800"] + Children: + GLOWSTONE_DUST: 5408 + REDSTONE: 5440 + SULPHUR: 21760 + 5408: # Potion of Health Boost II + Effects: ["HEALTH_BOOST 1 900"] + Children: + REDSTONE: 5440 + SULPHUR: 21792 + 5440: # Potion of Health Boost Extended + Effects: ["HEALTH_BOOST 0 4800"] + Children: + GLOWSTONE_DUST: 5408 + SULPHUR: 21824 + + 5632: # Potion of Absorption + Effects: ["ABSORPTION 0 1800"] + Children: + GLOWSTONE_DUST: 5664 + REDSTONE: 5696 + SULPHUR: 22016 + 5664: # Potion of Absorption II + Effects: ["ABSORPTION 1 900"] + Children: + REDSTONE: 5696 + SULPHUR: 22048 + 5696: # Potion of Absorption Extended + Effects: ["ABSORPTION 0 4800"] + Children: + GLOWSTONE_DUST: 5664 + SULPHUR: 22080 + + 5888: # Potion of Saturation + Effects: ["SATURATION 0 8"] + Children: + GLOWSTONE_DUST: 5920 + SULPHUR: 22272 + 5920: # Potion of Saturation II + Effects: ["SATURATION 1 8"] + Children: + REDSTONE: 5888 + SULPHUR: 22304 + + ### SPLASH POTIONS ######################################################### + + 16384: # Splash Mundane Potion + Children: + FERMENTED_SPIDER_EYE: 21000 + + 16642: # Splash Potion of Swiftness + Effects: ["SPEED 0 2700"] + Children: + FERMENTED_SPIDER_EYE: 16906 + GLOWSTONE_DUST: 16674 + REDSTONE: 16706 + 16674: # Splash Potion of Swiftness II + Effects: ["SPEED 1 1350"] + Children: + FERMENTED_SPIDER_EYE: 16906 + REDSTONE: 16706 + 16706: # Splash Potion of Swiftness Extended + Effects: ["SPEED 0 7200"] + Children: + FERMENTED_SPIDER_EYE: 16906 + GLOWSTONE_DUST: 16674 + + 16906: # Splash Potion of Slowness + Effects: ["SLOW 0 1350"] + Children: + REDSTONE: 16970 + 16970: # Splash Potion of Slowness Extended + Effects: ["SLOW 0 3600"] + Children: + GLOWSTONE_DUST: 16906 + + 17152: # Splash Potion of Haste + Effects: ["FAST_DIGGING 0 2700"] + Children: + GLOWSTONE_DUST: 17184 + REDSTONE: 17216 + 17184: # Splash Potion of Haste II + Effects: ["FAST_DIGGING 1 1350"] + Children: + REDSTONE: 17216 + 17216: # Splash Potion of Haste Extended + Effects: ["FAST_DIGGING 0 7200"] + Children: + GLOWSTONE_DUST: 17184 + + 17408: # Splash Potion of Dullness + Effects: ["SLOW_DIGGING 0 2700"] + Children: + GLOWSTONE_DUST: 17440 + REDSTONE: 17472 + 17440: # Splash Potion of Dullness II + Effects: ["SLOW_DIGGING 1 1350"] + Children: + REDSTONE: 17472 + 17472: # Splash Potion of Dullness Extended + Effects: ["SLOW_DIGGING 0 7200"] + Children: + GLOWSTONE_DUST: 17440 + + 17673: # Splash Potion of Strength + Effects: ["INCREASE_DAMAGE 0 2700"] + Children: + FERMENTED_SPIDER_EYE: 21000 + GLOWSTONE_DUST: 17705 + REDSTONE: 17737 + 17705: # Splash Potion of Strength II + Effects: ["INCREASE_DAMAGE 1 1350"] + Children: + FERMENTED_SPIDER_EYE: 21000 + REDSTONE: 17737 + 17737: # Splash Potion of Strength Extended + Effects: ["INCREASE_DAMAGE 0 7200"] + Children: + FERMENTED_SPIDER_EYE: 21064 + GLOWSTONE_DUST: 17705 + + 17925: # Splash Potion of Healing + Effects: ["HEAL 0"] + Children: + FERMENTED_SPIDER_EYE: 18188 + GLOWSTONE_DUST: 17957 + 17957: # Splash Potion of Healing II + Effects: ["HEAL 1"] + Children: + FERMENTED_SPIDER_EYE: 18220 + REDSTONE: 17925 + + 18188: # Splash Potion of Harming + Effects: ["HARM 0"] + Children: + GLOWSTONE_DUST: 18220 + 18220: # Splash Potion of Harming II + Effects: ["HARM 1"] + Children: + REDSTONE: 18188 + + 18432: # Splash Potion of Leaping + Effects: ["JUMP 0 2700"] + Children: + GLOWSTONE_DUST: 18464 + REDSTONE: 18496 + 18464: # Splash Potion of Leaping II + Effects: ["JUMP 1 1350"] + Children: + REDSTONE: 18496 + 18496: # Splash Potion of Leaping Extended + Effects: ["JUMP 0 7200"] + Children: + GLOWSTONE_DUST: 18464 + + 18688: # Splash Potion of Nausea + Effects: ["CONFUSION 0 338"] + Children: + REDSTONE: 18752 + 18752: # Splash Potion of Nausea Extended + Effects: ["CONFUSION 0 900"] + Children: + GLOWSTONE_DUST: 18688 + + 18945: # Splash Potion of Regeneration + Effects: ["REGENERATION 0 675"] + Children: + FERMENTED_SPIDER_EYE: 21000 + GLOWSTONE_DUST: 18977 + REDSTONE: 19009 + 18977: # Splash Potion of Regeneration II + Effects: ["REGENERATION 1 338"] + Children: + FERMENTED_SPIDER_EYE: 21000 + REDSTONE: 19009 + 19009: # Splash Potion of Regeneration Extended + Effects: ["REGENERATION 0 1800"] + Children: + FERMENTED_SPIDER_EYE: 21064 + GLOWSTONE_DUST: 18977 + + 19200: # Splash Potion of Resistance + Effects: ["DAMAGE_RESISTANCE 0 338"] + Children: + GLOWSTONE_DUST: 19232 + REDSTONE: 19264 + 19232: # Splash Potion of Resistance II + Effects: ["DAMAGE_RESISTANCE 1 169"] + Children: + REDSTONE: 19264 + 19264: # Splash Potion of Resistance Extended + Effects: ["DAMAGE_RESISTANCE 0 900"] + Children: + GLOWSTONE_DUST: 19232 + + 19459: # Splash Potion of Fire Resistance + Effects: ["FIRE_RESISTANCE 0 2700"] + Children: + FERMENTED_SPIDER_EYE: 16906 + REDSTONE: 19523 + 19523: # Splash Potion of Fire Resistance Extended + Effects: ["FIRE_RESISTANCE 0 7200"] + Children: + FERMENTED_SPIDER_EYE: 16970 + GLOWSTONE_DUST: 19459 + + 19725: # Splash Potion of Water Breathing + Effects: ["WATER_BREATHING 0 2700"] + Children: + REDSTONE: 19789 + 19789: # Splash Potion of Water Breathing Extended + Effects: ["WATER_BREATHING 0 7200"] + Children: + GLOWSTONE_DUST: 19725 + + 19982: # Splash Potion of Invisibility + Effects: ["INVISIBILITY 0 2700"] + Children: + REDSTONE: 20046 + 20046: # Splash Potion of Invisibility Extended + Effects: ["INVISIBILITY 0 7200"] + Children: + GLOWSTONE_DUST: 19982 + + 20224: # Splash Potion of Blindness + Effects: ["BLINDNESS 0 169"] + Children: + REDSTONE: 20288 + 20288: # Splash Potion of Blindness Extended + Effects: ["BLINDNESS 0 450"] + Children: + GLOWSTONE_DUST: 20224 + + 20486: # Splash Potion of Night Vision + Effects: ["NIGHT_VISION 0 2700"] + Children: + FERMENTED_SPIDER_EYE: 19982 + REDSTONE: 20550 + 20550: # Splash Potion of Night Vision Extended + Effects: ["NIGHT_VISION 0 7200"] + Children: + FERMENTED_SPIDER_EYE: 20046 + GLOWSTONE_DUST: 20486 + + 20736: # Splash Potion of Hunger + Effects: ["HUNGER 0 675"] + Children: + GLOWSTONE_DUST: 20768 + REDSTONE: 20800 + 20768: # Splash Potion of Hunger II + Effects: ["HUNGER 1 338"] + Children: + REDSTONE: 20800 + 20800: # Splash Potion of Hunger Extended + Effects: ["HUNGER 0 1800"] + Children: + GLOWSTONE_DUST: 20768 + + 21000: # Splash Potion of Weakness + Effects: ["WEAKNESS 0 1350"] + Children: + REDSTONE: 21064 + 21064: # Splash Potion of Weakness Extended + Effects: ["WEAKNESS 0 3600"] + Children: + GLOWSTONE_DUST: 21000 + + 21252: # Splash Potion of Poison + Effects: ["POISON 0 675"] + Children: + FERMENTED_SPIDER_EYE: 18188 + GLOWSTONE_DUST: 21284 + REDSTONE: 21316 + 21284: # Splash Potion of Poison II + Effects: ["POISON 1 338"] + Children: + FERMENTED_SPIDER_EYE: 18220 + REDSTONE: 21316 + 21316: # Splash Potion of Poison Extended + Effects: ["POISON 0 1800"] + Children: + FERMENTED_SPIDER_EYE: 18188 + GLOWSTONE_DUST: 21284 + + 21504: # Splash Potion of Decay + Effects: ["WITHER 0 338"] + Children: + GLOWSTONE_DUST: 21536 + REDSTONE: 21568 + 21536: # Splash Potion of Decay II + Effects: ["WITHER 1 169"] + Children: + REDSTONE: 21568 + 21568: # Splash Potion of Decay Extended + Effects: ["WITHER 0 900"] + Children: + GLOWSTONE_DUST: 21536 + + 21760: # Splash Potion of Health Boost + Effects: ["HEALTH_BOOST 0 1350"] + Children: + GLOWSTONE_DUST: 21792 + REDSTONE: 5440 + 21792: # Splash Potion of Health Boost II + Effects: ["HEALTH_BOOST 1 675"] + Children: + REDSTONE: 21824 + 21824: # Splash Potion of Health Boost Extended + Effects: ["HEALTH_BOOST 0 3600"] + Children: + GLOWSTONE_DUST: 21792 + + 22016: # Splash Potion of Absorption + Effects: ["ABSORPTION 0 1350"] + Children: + GLOWSTONE_DUST: 22048 + REDSTONE: 22080 + 22048: # Splash Potion of Absorption II + Effects: ["ABSORPTION 1 675"] + Children: + REDSTONE: 22080 + 22080: # Splash Potion of Absorption Extended + Effects: ["ABSORPTION 0 3600"] + Children: + GLOWSTONE_DUST: 22048 + + 22272: # Splash Potion of Saturation + Effects: ["SATURATION 0 6"] + Children: + GLOWSTONE_DUST: 22304 + 22304: # Splash Potion of Saturation II + Effects: ["SATURATION 1 6"] + Children: + REDSTONE: 22272