Skip to content

Commit

Permalink
Fix: Bonemeal is no longer infinite in survival mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
Torm committed May 6, 2020
1 parent cce6599 commit 0c9c637
Show file tree
Hide file tree
Showing 8 changed files with 504 additions and 330 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group 'no.hyp'
version '1.2.0'
version '1.2.1'

sourceCompatibility = 1.8

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* the plugin reads the configuration again. This allows a user to edit the configuration and see
* the changes in game without reloading the server.
*/
class ConfigurationWatcher implements Runnable {
final class ConfigurationWatcher implements Runnable {

private final FarmingUpgrade plugin;

Expand Down
436 changes: 109 additions & 327 deletions src/main/java/no/hyp/farmingupgrade/FarmingUpgrade.java

Large diffs are not rendered by default.

75 changes: 75 additions & 0 deletions src/main/java/no/hyp/farmingupgrade/FertiliseListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package no.hyp.farmingupgrade;

import com.google.common.collect.Lists;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFertilizeEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

import java.util.Collection;
import java.util.Random;
import java.util.function.Consumer;

final class FertiliseListener implements Listener {

private final FarmingUpgrade plugin;

private final Random random;

public FertiliseListener(FarmingUpgrade plugin, Random random) {
this.plugin = plugin;
this.random = random;
}

@EventHandler(priority = EventPriority.LOWEST)
public void onFertilise(BlockFertilizeEvent e) {
// Do not handle delegated BlockFertilizeEvents.
if (e instanceof UpgradedBlockFertilizeEvent) {
return;
}
// Only handle if upgraded fertilisation is enabled.
if (!this.plugin.configurationFertiliserUpgrade()) {
return;
}
Block block = e.getBlock();
// Upgraded fertilisation mechanics are only enabled for specific crops.
if (!this.plugin.fertilisableCrops.containsKey(block.getType())) {
return;
}
// Cancel to let plugin handle event.
e.setCancelled(true);
// Remove a bonemeal if not in creative.
if (e.getPlayer() != null && e.getPlayer().getGameMode() != GameMode.CREATIVE) {
Inventory inventory = e.getPlayer().getInventory();
int first = inventory.first(Material.BONE_MEAL);
ItemStack item = inventory.getItem(first);
if (item != null) {
item.setAmount(item.getAmount() - 1);
}
}
// Find adjacent fertilisable crops, send a BlockFertilizeEvent for them and apply
// fertiliser if the event is successful.
Collection<Block> fertilisedBlocks = FarmingUpgrade.findAdjacentMaterials(this.plugin.fertilisableCrops.keySet(), block, 1);
for (Block fertilisedBlock : fertilisedBlocks) {
BlockState fertilisedState = fertilisedBlock.getState();
// Apply fertiliser to the crop state.
Consumer<BlockState> fertiliserFunction = this.plugin.fertilisableCrops.get(fertilisedState.getType());
fertiliserFunction.accept(fertilisedState);
// Call an event for the fertilised block.
UpgradedBlockFertilizeEvent upgradedEvent = new UpgradedBlockFertilizeEvent(fertilisedBlock, e.getPlayer(), Lists.newArrayList(fertilisedState));
this.plugin.getServer().getPluginManager().callEvent(upgradedEvent);
// If the event is allowed, apply fertiliser.
if (!upgradedEvent.isCancelled()) {
fertilisedState.update();
FarmingUpgrade.fertiliseEffect(fertilisedBlock);
}
}
}

}
86 changes: 86 additions & 0 deletions src/main/java/no/hyp/farmingupgrade/HarvestListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package no.hyp.farmingupgrade;

import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;

import java.util.Random;
import java.util.Set;

final class HarvestListener implements Listener {

private final FarmingUpgrade plugin;

private final Random random;

public HarvestListener(FarmingUpgrade plugin, Random random) {
this.plugin = plugin;
this.random = random;
}

/**
* If a player breaks a crop with a hoe, handle this event manually. Find the adjacent crops
* and call custom UpgradedBlockBreakEvents on the to give other plugins a chance to catch them.
*
* Since this is a mechanic the event should be caught and cancelled as early as possible (LOWEST priority).
* Then we can dispatch new events that we can control the outcome of ourselves.
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onFarm(BlockBreakEvent e) {
if (!this.plugin.configurationHoeUpgrade()) {
return;
}
// Do not handle events that are called by FarmingUpgrade.
if (e instanceof UpgradedBlockBreakEvent) {
return;
}
Player player = e.getPlayer();
Block crop = e.getBlock();
// Handle breaking of crops.
if (!this.plugin.harvestableCrops.containsKey(crop.getType())) {
return;
}
ItemStack tool = player.getInventory().getItemInMainHand();
// If broken by a hoe, all crops within range are harvested and automatically replanted.
if (!this.plugin.tools.containsKey(tool.getType())) {
return;
}
e.setCancelled(true);
Vector direction = player.getLocation().getDirection();
//direction.add(new Vector(0.0, -direction.getY(), 0.0)).multiply(1.0 / direction.length());
Location location = player.getEyeLocation();
location.add(direction.multiply(1.5));
player.getWorld().spawnParticle(Particle.SWEEP_ATTACK, location, 1);
// Calculate the radius of the hoe sweep.
// Radius is affected by hoe material and the Efficiency enchantment (1 radius per 2 levels).
int range;
if (this.plugin.configurationHoeRange()) {
if (this.plugin.configurationHoeEfficiency()) {
range = Math.min(7, this.plugin.tools.get(tool.getType()) + tool.getEnchantmentLevel(Enchantment.DIG_SPEED) / 2);
} else {
range = Math.min(7, this.plugin.tools.get(tool.getType()));
}
} else {
range = 0;
}
// Get the adjacent crops in range.
Set<Block> adjacentCrops = FarmingUpgrade.findAdjacentMaterials(this.plugin.harvestableCrops.keySet(), crop, range);
// For every crop block, simulate a BlockBreakEvent for other plugins to react to.
boolean replant = this.plugin.configurationHoeReplant();
boolean unbreaking = this.plugin.configurationHoeUnbreaking();
boolean collect = this.plugin.configurationHoeCollect();
boolean harvest = this.plugin.configurationHoeHarvest();
for (Block adjacentCrop : adjacentCrops) {
FarmingUpgrade.harvestCrop(this.random, player, adjacentCrop, tool, replant, unbreaking, collect, harvest, this.plugin.harvestableCrops.get(adjacentCrop.getType()));
}
}

}
159 changes: 159 additions & 0 deletions src/main/java/no/hyp/farmingupgrade/HydrationListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package no.hyp.farmingupgrade;

import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockFadeEvent;
import org.bukkit.event.block.BlockGrowEvent;
import org.bukkit.event.block.MoistureChangeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.jetbrains.annotations.NotNull;

final class HydrationListener implements Listener {

private final FarmingUpgrade plugin;

/**
* When a player tramples farmland, Minecraft calls a PlayerInteractEvent, and then
* a BlockFadeEvent. Minecraft also calls FadeEvent when a Farmland is turning to
* Dirt from dehydration. To distinguish between trampling and drying, store the
* block that is trampled in InteractEvent here and check if it equal in the FadeEvent.
*/
private Block trampledFarmland;

public HydrationListener(FarmingUpgrade plugin) {
this.plugin = plugin;
}

/**
* Use this event to detect if the next BlockFadeEvent is caused by trampling.
* In that case, the hydration listener should not handle it.
*
* @param e
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onFarmlandTrample(@NotNull PlayerInteractEvent e) {
// Do not handle events that are delegated by FarmingUpgrade.
if (e instanceof UpgradedPlayerInteractEvent) {
return;
}
// Check that this is a Farmland trample event.
Block farmland = e.getClickedBlock();
boolean trample = farmland != null && farmland.getType() == Material.FARMLAND && e.getAction() == Action.PHYSICAL;
if (!trample) {
return;
}
// Store the farmland so the hydration listener does not handle it.
this.trampledFarmland = farmland;
}

/**
* A BlockFadeEvent is called when Minecraft wants to turn a Farmland block with a moisture level of 0
* into Dirt.
*
* It is also called after a PlayerInteractEvent, after a player tramples (jumps on) a
* Farmland block. This happens if upgraded trample mechanics is not enabled.
*
* Trample is handled elsewhere, so only handle drying here.
*
* @param e The event.
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onFarmlandDry(BlockFadeEvent e) {
// Do not handle events that are delegated by FarmingUpgrade.
if (e instanceof UpgradedBlockFadeEvent) {
return;
}
// Check that this is a Farmland dry or trample event.
Block farmland = e.getBlock();
if (farmland.getType() != Material.FARMLAND) {
return;
}
// Check if this block was trampled or if it is drying up.
// If this block is drying, apply upgraded hydration mechanics if they are enabled.
// If the block is trampled, let Minecraft handle it.
if (!farmland.equals(this.trampledFarmland)) {
// If upgraded hydration is enabled, cancel the event and handle it manually.
// Otherwise, let Minecraft handle the event as normal.
if (this.plugin.configurationHydrationUpgrade()) {
e.setCancelled(true);
int range = this.plugin.configurationHydrationRange();
int depth = this.plugin.configurationHydrationDepth();
int height = this.plugin.configurationHydrationHeight();
boolean dry = this.plugin.configurationHydrationDry();
FarmingUpgrade.farmlandUpgradedChangeMoisture(farmland, range, depth, height, dry);
}
}
// The trampled farmland check is no longer needed after handling the event.
this.trampledFarmland = null;
}

/**
* MoistureChangeEvent is called when Minecraft wants to change the moisture level of
* Farmland. Minecraft wants to dehydrate or dry (turn to Dirt) Farmland outside the
* Vanilla water range, and hydrate Farmland within the Vanilla water range.
*
* For blocks outside the Vanilla water range, but within the upgraded water range, cancel this
* event and hydrate the Farmland instead. Remember to also catch BlockFadeEvent for Farmland with the
* lowest humidity.
*
* For blocks inside the Vanilla water range, but outside the upgraded water range, dehydrate
* the Farmland instead. For fully hydrated Farmland, no event will be thrown. Use something
* like BlockGrowEvent to create events for fully hydrated Farmland.
*
* @param e
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onFarmlandMoistureChange(MoistureChangeEvent e) {
if (!this.plugin.configurationHydrationUpgrade()) {
return;
}
// Do not handle events that are called by FarmingUpgrade.
if (e instanceof UpgradedMoistureChangeEvent) {
return;
}
Block farmland = e.getBlock();
if (farmland.getType() != Material.FARMLAND) {
return;
}
e.setCancelled(true);
int range = this.plugin.configurationHydrationRange();
int depth = this.plugin.configurationHydrationDepth();
int height = this.plugin.configurationHydrationHeight();
boolean dry = this.plugin.configurationHydrationDry();
FarmingUpgrade.farmlandUpgradedChangeMoisture(farmland, range, depth, height, dry);
}

/**
* BlockGrowEvent is called when a crop grows.
*
* Minecraft does not send any events for fully hydrated Farmland within the Vanilla water range.
* Use BlockGrowEvents to create events for fully hydrated Farmland to use upgraded hydration mechanics.
*
* Minecraft does not dry Farmland into Dirt if there is a crop on it, and thus do not send
* any events either. Use the grow event to update the Farmland moisture.
*
* @param e
*/
@EventHandler(priority = EventPriority.MONITOR)
public void onCropGrow(BlockGrowEvent e) {
if (!this.plugin.configurationHydrationUpgrade()) {
return;
}
if (e.isCancelled()) {
return;
}
Block farmland = e.getBlock().getRelative(0, -1, 0);
if (farmland.getType() == Material.FARMLAND) {
int range = this.plugin.configurationHydrationRange();
int depth = this.plugin.configurationHydrationDepth();
int height = this.plugin.configurationHydrationHeight();
boolean dry = this.plugin.configurationHydrationDry();
FarmingUpgrade.farmlandUpgradedChangeMoisture(farmland, range, depth, height, dry);
}
}

}
Loading

0 comments on commit 0c9c637

Please sign in to comment.