diff --git a/Changelog.txt b/Changelog.txt index a7db9108c..93ee253ef 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,4 +1,7 @@ Version 2.2.017 + Fixed Blast Mining being almost completely broken + Reworked Blast Mining to drop non-mining related blocks too + Reworked Blast Mining to use your pickaxe when determining drops (will apply Silk Touch) Fixed shift-clicking ingredients into the brewing stand not working on older versions of Minecraft Added a setting in advanced.yml to ignore attack cooldowns (see notes) Fixed a bug with Mace permissions (thanks SrBedrock) diff --git a/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java b/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java index 9247cee3c..b92107e91 100644 --- a/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/skills/MiningCommand.java @@ -48,7 +48,7 @@ public class MiningCommand extends SkillCommand { blastMiningRank = miningManager.getBlastMiningTier(); bonusTNTDrops = miningManager.getDropMultiplier(); - oreBonus = percent.format(miningManager.getOreBonus() / 30.0D); // Base received in TNT is 30% + oreBonus = percent.format(miningManager.getOreBonus()); // Base received in TNT is 30% // debrisReduction = percent.format(miningManager.getDebrisReduction() / 30.0D); // Base received in TNT is 30% blastDamageDecrease = percent.format(miningManager.getBlastDamageModifier() / 100.0D); blastRadiusIncrease = miningManager.getBlastRadiusModifier(); diff --git a/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java b/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java index 3c2bc1d66..97706a989 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java +++ b/src/main/java/com/gmail/nossr50/datatypes/skills/subskills/acrobatics/Roll.java @@ -75,7 +75,8 @@ public class Roll extends AcrobaticsSubSkill { */ if (canRoll(mmoPlayer)) { - entityDamageEvent.setDamage(rollCheck(mmoPlayer, entityDamageEvent.getFinalDamage())); + entityDamageEvent.setDamage( + rollCheck(mmoPlayer, entityDamageEvent.getFinalDamage(), mmoPlayer.getPlayer().isSneaking())); if (entityDamageEvent.getFinalDamage() == 0) { entityDamageEvent.setCancelled(true); @@ -189,64 +190,24 @@ public class Roll extends AcrobaticsSubSkill { && Permissions.isSubSkillEnabled(mmoPlayer.getPlayer(), SubSkillType.ACROBATICS_ROLL); } - /** - * Handle the damage reduction and XP gain from the Roll ability - * - * @param damage The amount of damage initially dealt by the event - * @return the modified event damage if the ability was successful, the original event damage otherwise - */ - @VisibleForTesting - public double rollCheck(McMMOPlayer mmoPlayer, double damage) { - int skillLevel = mmoPlayer.getSkillLevel(getPrimarySkill()); - - if (mmoPlayer.getPlayer().isSneaking()) { - return gracefulRollCheck(mmoPlayer, damage, skillLevel); - } - - double modifiedDamage = calculateModifiedRollDamage(damage, mcMMO.p.getAdvancedConfig().getRollDamageThreshold()); - - if (!isFatal(mmoPlayer, modifiedDamage) - && ProbabilityUtil.isSkillRNGSuccessful(SubSkillType.ACROBATICS_ROLL, mmoPlayer)) { - NotificationManager.sendPlayerInformation(mmoPlayer.getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Roll.Text"); - SoundManager.sendCategorizedSound(mmoPlayer.getPlayer(), mmoPlayer.getPlayer().getLocation(), SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS); - //mmoPlayer.getPlayer().sendMessage(LocaleLoader.getString("Acrobatics.Roll.Text")); - - //if (!SkillUtils.cooldownExpired((long) mcMMOmmoPlayer.getPlayer().getTeleportATS(), Config.getInstance().getXPAfterTeleportCooldown())) { - if (!isExploiting(mmoPlayer) && mmoPlayer.getAcrobaticsManager().canGainRollXP()) - SkillUtils.applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, damage, true), XPGainReason.PVE); - //} - - addFallLocation(mmoPlayer); - return modifiedDamage; - } else if (!isFatal(mmoPlayer, damage)) { - //if (!SkillUtils.cooldownExpired((long) mmoPlayer.getTeleportATS(), Config.getInstance().getXPAfterTeleportCooldown())) { - if (!isExploiting(mmoPlayer) && mmoPlayer.getAcrobaticsManager().canGainRollXP()) - SkillUtils.applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, damage, false), XPGainReason.PVE); - //} - } - - addFallLocation(mmoPlayer); - return damage; - } - private int getActivationChance(McMMOPlayer mmoPlayer) { return PerksUtils.handleLuckyPerks(mmoPlayer, getPrimarySkill()); } /** - * Handle the damage reduction and XP gain from the Graceful Roll ability + * Handle the damage reduction and XP gain from the Roll / Graceful Roll ability * * @param damage The amount of damage initially dealt by the event * @return the modified event damage if the ability was successful, the original event damage otherwise */ - private double gracefulRollCheck(McMMOPlayer mmoPlayer, double damage, int skillLevel) { - double modifiedDamage = calculateModifiedRollDamage(damage, mcMMO.p.getAdvancedConfig().getRollDamageThreshold() * 2); - - final Probability gracefulProbability = getGracefulProbability(mmoPlayer); + private double rollCheck(McMMOPlayer mmoPlayer, double damage, boolean isGracefulRoll) { + final Probability probability + = isGracefulRoll ? getGracefulProbability(mmoPlayer) : getNonGracefulProbability(mmoPlayer); + double modifiedDamage = calculateModifiedRollDamage(damage, + mcMMO.p.getAdvancedConfig().getRollDamageThreshold() * 2); if (!isFatal(mmoPlayer, modifiedDamage) - //TODO: Graceful isn't sending out an event - && ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.ACROBATICS, mmoPlayer, gracefulProbability)) { + && ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.ACROBATICS, mmoPlayer, probability)) { NotificationManager.sendPlayerInformation(mmoPlayer.getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Ability.Proc"); SoundManager.sendCategorizedSound(mmoPlayer.getPlayer(), mmoPlayer.getPlayer().getLocation(), SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS,0.5F); if (!isExploiting(mmoPlayer) && mmoPlayer.getAcrobaticsManager().canGainRollXP()) @@ -270,6 +231,11 @@ public class Roll extends AcrobaticsSubSkill { return Probability.ofValue(gracefulOdds); } + public static Probability getNonGracefulProbability(McMMOPlayer mmoPlayer) { + double gracefulOdds = ProbabilityUtil.getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer).getValue(); + return Probability.ofValue(gracefulOdds); + } + /** * Check if the player is "farming" Acrobatics XP using * exploits in the game. diff --git a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java index 8c3e746ac..72ee68df5 100644 --- a/src/main/java/com/gmail/nossr50/listeners/EntityListener.java +++ b/src/main/java/com/gmail/nossr50/listeners/EntityListener.java @@ -756,7 +756,7 @@ public class EntityListener implements Listener { if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) return; - Entity entity = event.getEntity(); + final Entity entity = event.getEntity(); if (!(entity instanceof TNTPrimed) || !entity.hasMetadata(MetadataConstants.METADATA_KEY_TRACKED_TNT)) { return; @@ -764,13 +764,14 @@ public class EntityListener implements Listener { // We can make this assumption because we (should) be the only ones // using this exact metadata - Player player = pluginRef.getServer().getPlayerExact(entity.getMetadata(MetadataConstants.METADATA_KEY_TRACKED_TNT).get(0).asString()); + final Player player = pluginRef.getServer().getPlayerExact( + entity.getMetadata(MetadataConstants.METADATA_KEY_TRACKED_TNT).get(0).asString()); if (!UserManager.hasPlayerDataKey(player)) { return; } - //Profile not loaded + // Profile is not loaded if (UserManager.getPlayer(player) == null) { return; } @@ -781,7 +782,7 @@ public class EntityListener implements Listener { return; } - MiningManager miningManager = UserManager.getPlayer(player).getMiningManager(); + final MiningManager miningManager = UserManager.getPlayer(player).getMiningManager(); if (miningManager.canUseBiggerBombs()) { event.setRadius(miningManager.biggerBombs(event.getRadius())); diff --git a/src/main/java/com/gmail/nossr50/skills/mining/BlastMining.java b/src/main/java/com/gmail/nossr50/skills/mining/BlastMining.java index 76cf6f8c6..95b853548 100644 --- a/src/main/java/com/gmail/nossr50/skills/mining/BlastMining.java +++ b/src/main/java/com/gmail/nossr50/skills/mining/BlastMining.java @@ -10,58 +10,17 @@ import org.bukkit.entity.TNTPrimed; import org.bukkit.event.entity.EntityDamageByEntityEvent; public class BlastMining { - // The order of the values is extremely important, a few methods depend on it to work properly - /* 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; - } - - protected int getLevel() { - return mcMMO.p.getAdvancedConfig().getBlastMiningRankLevel(this); - } - - - }*/ - public final static int MAXIMUM_REMOTE_DETONATION_DISTANCE = 100; public static double getBlastRadiusModifier(int rank) { return mcMMO.p.getAdvancedConfig().getBlastRadiusModifier(rank); } - - public static double getBlastDamageDecrease(int rank) { return mcMMO.p.getAdvancedConfig().getBlastDamageDecrease(rank); } - - public static int getDemolitionExpertUnlockLevel() { - /*List tierList = Arrays.asList(Tier.values()); - for (Tier tier : tierList) { - if (tier.getBlastDamageDecrease() > 0) { - continue; - } - - return tier == Tier.EIGHT ? tier.getLevel() : tierList.get(tierList.indexOf(tier) - 1).getLevel(); - }*/ - for(int i = 0; i < SubSkillType.MINING_BLAST_MINING.getNumRanks()-1; i++) { if (getBlastDamageDecrease(i+1) > 0) return RankUtils.getRankUnlockLevel(SubSkillType.MINING_BLAST_MINING, i+1); @@ -71,15 +30,6 @@ public class BlastMining { } public static int getBiggerBombsUnlockLevel() { - /*List tierList = Arrays.asList(Tier.values()); - for (Tier tier : tierList) { - if (tier.getBlastRadiusModifier() > 1.0) { - continue; - } - - return tier == Tier.EIGHT ? tier.getLevel() : tierList.get(tierList.indexOf(tier) - 1).getLevel(); - }*/ - for(int i = 0; i < SubSkillType.MINING_BLAST_MINING.getNumRanks()-1; i++) { if (getBlastRadiusModifier(i+1) > 0) return RankUtils.getRankUnlockLevel(SubSkillType.MINING_BLAST_MINING, i+1); @@ -104,7 +54,7 @@ public class BlastMining { return false; } - MiningManager miningManager = UserManager.getPlayer(defender).getMiningManager(); + final MiningManager miningManager = UserManager.getPlayer(defender).getMiningManager(); if (!miningManager.canUseDemolitionsExpertise()) { return false; diff --git a/src/main/java/com/gmail/nossr50/skills/mining/MiningManager.java b/src/main/java/com/gmail/nossr50/skills/mining/MiningManager.java index 07d934b98..59e96a8e9 100644 --- a/src/main/java/com/gmail/nossr50/skills/mining/MiningManager.java +++ b/src/main/java/com/gmail/nossr50/skills/mining/MiningManager.java @@ -17,7 +17,6 @@ import com.gmail.nossr50.util.random.Probability; import com.gmail.nossr50.util.random.ProbabilityUtil; import com.gmail.nossr50.util.skills.RankUtils; import com.gmail.nossr50.util.skills.SkillUtils; -import org.apache.commons.lang3.RandomUtils; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockState; @@ -30,8 +29,11 @@ import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import static com.gmail.nossr50.util.ItemUtils.isPickaxe; + public class MiningManager extends SkillManager { public static final String BUDDING_AMETHYST = "budding_amethyst"; @@ -51,7 +53,7 @@ public class MiningManager extends SkillManager { Player player = getPlayer(); return canUseBlastMining() && player.isSneaking() - && (ItemUtils.isPickaxe(getPlayer().getInventory().getItemInMainHand()) || player.getInventory().getItemInMainHand().getType() == mcMMO.p.getGeneralConfig().getDetonatorItem()) + && (isPickaxe(getPlayer().getInventory().getItemInMainHand()) || player.getInventory().getItemInMainHand().getType() == mcMMO.p.getGeneralConfig().getDetonatorItem()) && Permissions.remoteDetonation(player); } @@ -168,69 +170,76 @@ public class MiningManager extends SkillManager { * @param yield The % of blocks to drop * @param event The {@link EntityExplodeEvent} */ - //TODO: Rewrite this garbage public void blastMiningDropProcessing(float yield, EntityExplodeEvent event) { if (yield == 0) return; - //Strip out only stuff that gives mining XP + var increasedYieldFromBonuses = yield + (yield * getOreBonus()); + // Strip out only stuff that gives mining XP List ores = new ArrayList<>(); - List notOres = new ArrayList<>(); for (Block targetBlock : event.blockList()) { BlockState blockState = targetBlock.getState(); - //Containers usually have 0 XP unless someone edited their config in a very strange way - if (ExperienceConfig.getInstance().getXp(PrimarySkillType.MINING, targetBlock) != 0 - && !(targetBlock instanceof Container) - && !mcMMO.getUserBlockTracker().isIneligible(targetBlock)) { - if (BlockUtils.isOre(blockState)) { + + if(mcMMO.getUserBlockTracker().isIneligible(targetBlock)) + continue; + + if (ExperienceConfig.getInstance().getXp(PrimarySkillType.MINING, targetBlock) != 0) { + if (BlockUtils.isOre(blockState) && !(targetBlock instanceof Container)) { ores.add(blockState); - } else { - notOres.add(blockState); } + } else { + notOres.add(blockState); } } int xp = 0; - - float oreBonus = (float) (getOreBonus() / 100); - float debrisReduction = (float) (getDebrisReduction() / 100); int dropMultiplier = getDropMultiplier(); - float debrisYield = yield - debrisReduction; - //Drop "debris" based on skill modifiers for(BlockState blockState : notOres) { if (isDropIllegal(blockState.getType())) continue; if (Probability.ofPercent(50).evaluate()) { - ItemUtils.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_DEBRIS_NON_ORES); // Initial block that would have been dropped + ItemUtils.spawnItem(getPlayer(), + Misc.getBlockCenter(blockState), + new ItemStack(blockState.getType()), + ItemSpawnReason.BLAST_MINING_DEBRIS_NON_ORES); // Initial block that would have been dropped } } - for (BlockState blockState : ores) { - if (isDropIllegal(blockState.getType())) + // currentOreYield only used for drop calculations for ores + float currentOreYield = increasedYieldFromBonuses; + + if (isDropIllegal(blockState.getType())) { continue; + } - if (RandomUtils.nextFloat() < (yield + oreBonus)) { - xp += Mining.getBlockXp(blockState); + // Always give XP for every ore destroyed + xp += Mining.getBlockXp(blockState); + while(currentOreYield > 0) { + if (Probability.ofValue(currentOreYield).evaluate()) { + Collection oreDrops = isPickaxe(mmoPlayer.getPlayer().getInventory().getItemInMainHand()) + ? blockState.getBlock().getDrops(mmoPlayer.getPlayer().getInventory().getItemInMainHand()) + : List.of(new ItemStack(blockState.getType())); + ItemUtils.spawnItems(getPlayer(), Misc.getBlockCenter(blockState), + oreDrops, ItemSpawnReason.BLAST_MINING_ORES); - ItemUtils.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES); // Initial block that would have been dropped - - if (mcMMO.p.getAdvancedConfig().isBlastMiningBonusDropsEnabled() && !mcMMO.getUserBlockTracker().isIneligible(blockState)) { - for (int i = 1; i < dropMultiplier; i++) { -// Bukkit.broadcastMessage("Bonus Drop on Ore: "+blockState.getType().toString()); - ItemUtils.spawnItem(getPlayer(), Misc.getBlockCenter(blockState), new ItemStack(blockState.getType()), ItemSpawnReason.BLAST_MINING_ORES_BONUS_DROP); // Initial block that would have been dropped + if (mcMMO.p.getAdvancedConfig().isBlastMiningBonusDropsEnabled()) { + for (int i = 1; i < dropMultiplier; i++) { + ItemUtils.spawnItems(getPlayer(), + Misc.getBlockCenter(blockState), + oreDrops, + ItemSpawnReason.BLAST_MINING_ORES_BONUS_DROP); + } } } + currentOreYield = Math.max(currentOreYield - 1, 0); } } - //Replace the event blocklist with the newYield list + // Replace the event blocklist with the newYield list event.setYield(0F); -// event.blockList().clear(); -// event.blockList().addAll(notOres); - applyXpGain(xp, XPGainReason.PVE); } @@ -273,10 +282,11 @@ public class MiningManager extends SkillManager { * * @return the Blast Mining tier */ - public double getOreBonus() { - return getOreBonus(getBlastMiningTier()); + public float getOreBonus() { + return (float) (mcMMO.p.getAdvancedConfig().getOreBonus(getBlastMiningTier()) / 100F); } + @Deprecated(since = "2.2.017", forRemoval = true) public static double getOreBonus(int rank) { return mcMMO.p.getAdvancedConfig().getOreBonus(rank); } diff --git a/src/main/java/com/gmail/nossr50/util/ItemUtils.java b/src/main/java/com/gmail/nossr50/util/ItemUtils.java index deaf95e93..60bb75069 100644 --- a/src/main/java/com/gmail/nossr50/util/ItemUtils.java +++ b/src/main/java/com/gmail/nossr50/util/ItemUtils.java @@ -681,6 +681,21 @@ public final class ItemUtils { return enchantmentWrappers.get(randomIndex); } + /** + * Drop items at a given location. + * + * @param location The location to drop the items at + * @param itemStacks The items to drop + */ + public static void spawnItems(@Nullable Player player, + @NotNull Location location, + @NotNull Collection itemStacks, + @NotNull ItemSpawnReason itemSpawnReason) { + for (ItemStack is : itemStacks) { + spawnItem(player, location, is, itemSpawnReason); + } + } + /** * Drop items at a given location. * diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index af1fd69c9..bae179cd4 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -165,8 +165,6 @@ Alchemy.Listener=Alchemy: Alchemy.Ability.Locked.0=LOCKED UNTIL {0}+ SKILL (CATALYSIS) Alchemy.SkillName=ALCHEMY #ARCHERY - - Archery.SubSkill.SkillShot.Name=Skill Shot Archery.SubSkill.SkillShot.Description=Increases damage done with bows Archery.SubSkill.SkillShot.Stat=Skill Shot Bonus Damage