Jelajahi Sumber

Immature crop replanting, green thumb tweaks, replant accidental break
protection

nossr50 5 tahun lalu
induk
melakukan
e2073ff9f7

+ 2 - 0
Changelog.txt

@@ -1,4 +1,6 @@
 Version 2.1.115
+    Hoes no longer give free replants
+    There is now a feature in place to prevent breaking a newly automatically replanted (via green thumb) crop from being breakable for a few seconds after it appears
     Using a hoe on non-fully grown crops will replant them as a convenience feature for those who can't bother to wait for all of their plants to grow (put away the hoe to break non-fully grown crops)
     Fixed a bug where Salvage always gave the best results
     Fixed an issue with arrows causing exceptions with players not yet having data loaded

+ 1 - 0
src/main/java/com/gmail/nossr50/datatypes/meta/OldName.java

@@ -12,4 +12,5 @@ public class OldName extends FixedMetadataValue {
     {
         super(plugin, oldName);
     }
+
 }

+ 17 - 0
src/main/java/com/gmail/nossr50/datatypes/meta/RecentlyReplantedCropMeta.java

@@ -0,0 +1,17 @@
+package com.gmail.nossr50.datatypes.meta;
+
+import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.plugin.Plugin;
+
+public class RecentlyReplantedCropMeta extends FixedMetadataValue {
+
+    /**
+     * Initializes a FixedMetadataValue with an Object
+     *
+     * @param owningPlugin the {@link Plugin} that created this metadata value
+     */
+    public RecentlyReplantedCropMeta(Plugin owningPlugin) {
+        super(owningPlugin, true);
+    }
+
+}

+ 1 - 0
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -107,6 +107,7 @@ public class mcMMO extends JavaPlugin {
     private static boolean isRetroModeEnabled;
 
     /* Metadata Values */
+    public final static String REPLANT_META_KEY = "mcMMO: Recently Replanted";
     public static final String FISH_HOOK_REF_METAKEY = "mcMMO: Fish Hook Tracker";
     public static final String DODGE_TRACKER        = "mcMMO: Dodge Tracker";
     public static final String CUSTOM_DAMAGE_METAKEY = "mcMMO: Custom Damage";

+ 90 - 0
src/main/java/com/gmail/nossr50/runnables/skills/DelayedCropReplant.java

@@ -0,0 +1,90 @@
+package com.gmail.nossr50.runnables.skills;
+
+import com.gmail.nossr50.mcMMO;
+import com.gmail.nossr50.util.skills.ParticleEffectUtils;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.block.data.Ageable;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+public class DelayedCropReplant extends BukkitRunnable {
+
+    private final int desiredCropAge;
+    private final Location cropLocation;
+    private final Material cropMaterial;
+    private boolean wasImmaturePlant;
+    private final BlockBreakEvent blockBreakEvent;
+
+    /**
+     * Replants a crop after a delay setting the age to desiredCropAge
+     * @param cropState target {@link BlockState}
+     * @param desiredCropAge desired age of the crop
+     */
+    public DelayedCropReplant(BlockBreakEvent blockBreakEvent, BlockState cropState, int desiredCropAge, boolean wasImmaturePlant) {
+        //The plant was either immature or something cancelled the event, therefor we need to treat it differently
+        this.blockBreakEvent = blockBreakEvent;
+        this.wasImmaturePlant = wasImmaturePlant;
+        this.cropMaterial = cropState.getType();
+        this.desiredCropAge = desiredCropAge;
+        this.cropLocation = cropState.getLocation();
+    }
+
+    @Override
+    public void run() {
+        Block cropBlock = cropLocation.getBlock();
+        BlockState currentState = cropBlock.getState();
+
+        if(blockBreakEvent.isCancelled()) {
+            wasImmaturePlant = true;
+        }
+
+        //Two kinds of air in Minecraft
+        if(currentState.getType().equals(Material.AIR) || currentState.getType().equals(Material.CAVE_AIR)) {
+            //The space is not currently occupied by a block so we can fill it
+            cropBlock.setType(cropMaterial);
+
+            //Get new state (necessary?)
+            BlockState newState = cropBlock.getState();
+            newState.setType(cropMaterial);
+            newState.update();
+            Ageable ageable = (Ageable) newState.getBlockData();
+
+            //Crop age should always be 0 if the plant was immature
+            if(wasImmaturePlant) {
+                ageable.setAge(0);
+            } else {
+                //Otherwise make the plant the desired age
+                ageable.setAge(desiredCropAge);
+            }
+
+            //Age the crop
+            newState.setBlockData(ageable);
+
+            //Play an effect
+            ParticleEffectUtils.playGreenThumbEffect(cropLocation);
+
+            //Remove the metadata marking the block as recently replanted
+            new removePlantMeta(blockBreakEvent.getBlock().getLocation()).runTaskLater(mcMMO.p, 20);
+        }
+
+    }
+
+    private class removePlantMeta extends BukkitRunnable {
+
+        private final Location cropLoc;
+
+        public removePlantMeta(Location cropLoc) {
+            this.cropLoc = cropLoc;
+        }
+
+        @Override
+        public void run() {
+            Block cropBlock = cropLoc.getBlock();
+            cropBlock.removeMetadata(mcMMO.REPLANT_META_KEY, mcMMO.p);
+        }
+    }
+
+}

+ 52 - 23
src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java

@@ -7,6 +7,7 @@ import com.gmail.nossr50.datatypes.BlockSnapshot;
 import com.gmail.nossr50.datatypes.experience.XPGainReason;
 import com.gmail.nossr50.datatypes.experience.XPGainSource;
 import com.gmail.nossr50.datatypes.interactions.NotificationType;
+import com.gmail.nossr50.datatypes.meta.RecentlyReplantedCropMeta;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
 import com.gmail.nossr50.datatypes.skills.SubSkillType;
@@ -14,6 +15,7 @@ import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
 import com.gmail.nossr50.datatypes.skills.ToolType;
 import com.gmail.nossr50.datatypes.treasure.HylianTreasure;
 import com.gmail.nossr50.mcMMO;
+import com.gmail.nossr50.runnables.skills.DelayedCropReplant;
 import com.gmail.nossr50.runnables.skills.DelayedHerbalismXPCheckTask;
 import com.gmail.nossr50.runnables.skills.HerbalismBlockUpdaterTask;
 import com.gmail.nossr50.skills.SkillManager;
@@ -158,6 +160,13 @@ public class HerbalismManager extends SkillManager {
     private void processHerbalismOnBlocksBroken(BlockBreakEvent blockBreakEvent, HashSet<Block> brokenPlants) {
         BlockState originalBreak = blockBreakEvent.getBlock().getState();
 
+        //Check if the plant was recently replanted
+        if(blockBreakEvent.getBlock().getMetadata(mcMMO.REPLANT_META_KEY).size() >= 1) {
+            //Crop is recently replanted to back out of destroying it
+            blockBreakEvent.setCancelled(true);
+            return;
+        }
+
         //TODO: The design of Green Terra needs to change, this is a mess
         if(Permissions.greenThumbPlant(getPlayer(), originalBreak.getType())) {
             processGreenThumbPlants(originalBreak, blockBreakEvent, isGreenTerraActive());
@@ -639,6 +648,18 @@ public class HerbalismManager extends SkillManager {
         return Herbalism.convertShroomThumb(blockState);
     }
 
+    /**
+     * Starts the delayed replant task and turns
+     * @param desiredCropAge the desired age of the crop
+     * @param blockBreakEvent the {@link BlockBreakEvent} this crop was involved in
+     * @param cropState the {@link BlockState} of the crop
+     */
+    private void startReplantTask(int desiredCropAge, BlockBreakEvent blockBreakEvent, BlockState cropState, boolean isImmature) {
+        //Mark the plant as recently replanted to avoid accidental breakage
+        blockBreakEvent.getBlock().setMetadata(mcMMO.REPLANT_META_KEY, new RecentlyReplantedCropMeta(mcMMO.p));
+        new DelayedCropReplant(blockBreakEvent, cropState, desiredCropAge, isImmature).runTaskLater(mcMMO.p, 20 * 2);
+    }
+
     /**
      * Process the Green Thumb ability for plants.
      *
@@ -651,12 +672,13 @@ public class HerbalismManager extends SkillManager {
         if (!(blockData instanceof Ageable))
             return;
 
+        Ageable ageable = (Ageable) blockData;
+
         //If the ageable is NOT mature and the player is NOT using a hoe, abort
-        if(!isAgeableMature((Ageable) blockData) && !ItemUtils.isHoe(getPlayer().getItemInHand())) {
+        if(!isAgeableMature(ageable) && !ItemUtils.isHoe(getPlayer().getItemInHand())) {
             return;
         }
 
-
         Player player = getPlayer();
         PlayerInventory playerInventory = player.getInventory();
         Material seed = null;
@@ -696,31 +718,36 @@ public class HerbalismManager extends SkillManager {
             return;
         }
 
-        if (!processGrowingPlants(blockState, blockBreakEvent, greenTerra)) {
+
+        if (!playerInventory.containsAtLeast(seedStack, 1)) {
             return;
         }
 
-        if(!ItemUtils.isHoe(getPlayer().getInventory().getItemInMainHand()))
-        {
-            if (!playerInventory.containsAtLeast(seedStack, 1)) {
-                return;
-            }
-
-            playerInventory.removeItem(seedStack);
-            player.updateInventory(); // Needed until replacement available
+        if (!processGrowingPlants(blockState, ageable, blockBreakEvent, greenTerra)) {
+            return;
         }
 
+        playerInventory.removeItem(seedStack);
+        player.updateInventory(); // Needed until replacement available
+
         new HerbalismBlockUpdaterTask(blockState).runTaskLater(mcMMO.p, 0);
     }
 
-    private boolean processGrowingPlants(BlockState blockState, BlockBreakEvent blockBreakEvent, boolean greenTerra) {
-        Ageable crops = (Ageable) blockState.getBlockData();
+    private boolean processGrowingPlants(BlockState blockState, Ageable ageable, BlockBreakEvent blockBreakEvent, boolean greenTerra) {
+        //This check is needed
+        if(isBizarreAgeable(ageable)) {
+            return false;
+        }
+
+        int finalAge = 0;
         int greenThumbStage = getGreenThumbStage();
 
         //Immature plants will start over at 0
-        if(!isAgeableMature(crops)) {
-            crops.setAge(0);
+        if(!isAgeableMature(ageable)) {
             blockBreakEvent.setCancelled(true);
+            startReplantTask(0, blockBreakEvent, blockState, true);
+            blockState.setType(Material.AIR);
+            blockState.update();
             return true;
         }
 
@@ -733,10 +760,10 @@ public class HerbalismManager extends SkillManager {
             case WHEAT:
 
                 if (greenTerra) {
-                    crops.setAge(3);
+                    finalAge = 3;
                 }
                 else {
-                    crops.setAge(greenThumbStage);
+                    finalAge = getGreenThumbStage();
                 }
                 break;
 
@@ -744,30 +771,32 @@ public class HerbalismManager extends SkillManager {
             case NETHER_WART:
 
                 if (greenTerra || greenThumbStage > 2) {
-                    crops.setAge(2);
+                    finalAge = 2;
                 }
                 else if (greenThumbStage == 2) {
-                    crops.setAge(1);
+                    finalAge = 1;
                 }
                 else {
-                    crops.setAge(0);
+                    finalAge = 0;
                 }
                break;
 
             case COCOA:
 
                 if (greenTerra || getGreenThumbStage() > 1) {
-                    crops.setAge(1);
+                    finalAge = 1;
                 }
                 else {
-                    crops.setAge(0);
+                    finalAge = 0;
                 }
                 break;
 
             default:
                 return false;
         }
-        blockState.setBlockData(crops);
+
+        //Start the delayed replant
+        startReplantTask(finalAge, blockBreakEvent, blockState, false);
         return true;
     }
 

+ 11 - 4
src/main/java/com/gmail/nossr50/util/skills/ParticleEffectUtils.java

@@ -1,6 +1,8 @@
 package com.gmail.nossr50.util.skills;
 
 import com.gmail.nossr50.config.Config;
+import com.gmail.nossr50.util.sounds.SoundManager;
+import com.gmail.nossr50.util.sounds.SoundType;
 import org.bukkit.Effect;
 import org.bukkit.Location;
 import org.bukkit.Material;
@@ -14,6 +16,12 @@ public final class ParticleEffectUtils {
 
     private ParticleEffectUtils() {};
 
+    public static void playGreenThumbEffect(Location location) {
+        World world = location.getWorld();
+        playSmokeEffect(location);
+        SoundManager.worldSendSoundMaxPitch(world, location, SoundType.POP);
+    }
+
     public static void playBleedEffect(LivingEntity livingEntity) {
         if (!Config.getInstance().getBleedEffectEnabled()) {
             return;
@@ -27,7 +35,7 @@ public final class ParticleEffectUtils {
             return;
         }
 
-        playSmokeEffect(player);
+        playSmokeEffect(player.getLocation());
     }
 
     public static void playFluxEffect(Location location) {
@@ -38,9 +46,8 @@ public final class ParticleEffectUtils {
         location.getWorld().playEffect(location, Effect.MOBSPAWNER_FLAMES, 1);
     }
 
-    public static void playSmokeEffect(LivingEntity livingEntity) {
-        Location location = livingEntity.getEyeLocation();
-        World world = livingEntity.getWorld();
+    public static void playSmokeEffect(Location location) {
+        World world = location.getWorld();
 
         // Have to do it this way, because not all block directions are valid for smoke
         world.playEffect(location, Effect.SMOKE, BlockFace.SOUTH_EAST);

+ 5 - 0
src/main/java/com/gmail/nossr50/util/sounds/SoundManager.java

@@ -39,6 +39,11 @@ public class SoundManager {
             world.playSound(location, getSound(soundType), getVolume(soundType), getPitch(soundType));
     }
 
+    public static void worldSendSoundMaxPitch(World world, Location location, SoundType soundType) {
+        if(SoundConfig.getInstance().getIsEnabled(soundType))
+            world.playSound(location, getSound(soundType), getVolume(soundType), 2.0F);
+    }
+
     /**
      * All volume is multiplied by the master volume to get its final value
      * @param soundType target soundtype