Immature crop replanting, green thumb tweaks, replant accidental break

protection
This commit is contained in:
nossr50 2020-02-19 15:58:53 -08:00
parent 8f26544188
commit e2073ff9f7
8 changed files with 179 additions and 27 deletions

View File

@ -1,4 +1,6 @@
Version 2.1.115 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) 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 a bug where Salvage always gave the best results
Fixed an issue with arrows causing exceptions with players not yet having data loaded Fixed an issue with arrows causing exceptions with players not yet having data loaded

View File

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

View File

@ -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);
}
}

View File

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

View File

@ -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);
}
}
}

View File

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

View File

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

View File

@ -39,6 +39,11 @@ public class SoundManager {
world.playSound(location, getSound(soundType), getVolume(soundType), getPitch(soundType)); 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 * All volume is multiplied by the master volume to get its final value
* @param soundType target soundtype * @param soundType target soundtype