bentobox/src/main/java/world/bentobox/bentobox/listeners/flags/protection/BlockInteractionListener.java

305 lines
11 KiB
Java

package world.bentobox.bentobox.listeners.flags.protection;
import java.util.Optional;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BrushableBlock;
import org.bukkit.block.Sign;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockFromToEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import world.bentobox.bentobox.api.flags.FlagListener;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.lists.Flags;
/**
* Handle interaction with blocks
* @author tastybento
*/
public class BlockInteractionListener extends FlagListener
{
/**
* Handle interaction with blocks
*
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerInteract(final PlayerInteractEvent e)
{
// We only care about the RIGHT_CLICK_BLOCK action.
if (!e.getAction().equals(Action.RIGHT_CLICK_BLOCK) || e.getClickedBlock() == null)
{
return;
}
// Check clicked block
this.checkClickedBlock(e, e.getPlayer(), e.getClickedBlock());
// Now check for in-hand items
if (e.getItem() != null && !e.getItem().getType().equals(Material.AIR))
{
// Boats
if (Tag.ITEMS_BOATS.isTagged(e.getItem().getType()))
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BOAT);
}
else if (e.getItem().getType().name().endsWith("_SPAWN_EGG"))
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.SPAWN_EGGS);
}
else if (e.getItem().getType() == Material.ENDER_PEARL)
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.ENDER_PEARL);
}
else if (e.getItem().getType() == Material.BONE_MEAL)
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.PLACE_BLOCKS);
}
else if (e.getItem().getType() == Material.GLASS_BOTTLE)
{
Block targetedBlock = e.getPlayer().getTargetBlockExact(5, FluidCollisionMode.ALWAYS);
// Check if player is clicking on water or waterlogged block with a bottle.
if (targetedBlock != null && (Material.WATER.equals(targetedBlock.getType()) ||
targetedBlock.getBlockData() instanceof Waterlogged))
{
this.checkIsland(e, e.getPlayer(), e.getClickedBlock().getLocation(), Flags.BREWING);
}
}
}
}
/**
* Check if an action can occur on a clicked block
*
* @param e - event called
* @param player - player
* @param block - block being clicked or used
*/
private void checkClickedBlock(Event e, Player player, Block block)
{
if (checkSpecialCases(e, player, block) || checkTags(e, player, block)) {
return;
}
Material type = block.getType();
Location loc = block.getLocation();
switch (type)
{
case BEACON -> this.checkIsland(e, player, loc, Flags.BEACON);
case BREWING_STAND -> this.checkIsland(e, player, loc, Flags.BREWING);
case BEEHIVE, BEE_NEST -> this.checkIsland(e, player, loc, Flags.HIVE);
case BARREL -> this.checkIsland(e, player, loc, Flags.BARREL);
case CHEST, CHEST_MINECART -> this.checkIsland(e, player, loc, Flags.CHEST);
case TRAPPED_CHEST -> this.checkIsland(e, player, loc, Flags.TRAPPED_CHEST);
case FLOWER_POT -> this.checkIsland(e, player, loc, Flags.FLOWER_POT);
case COMPOSTER -> this.checkIsland(e, player, loc, Flags.COMPOSTER);
case DISPENSER -> this.checkIsland(e, player, loc, Flags.DISPENSER);
case DROPPER -> this.checkIsland(e, player, loc, Flags.DROPPER);
case HOPPER, HOPPER_MINECART -> this.checkIsland(e, player, loc, Flags.HOPPER);
case BLAST_FURNACE, CAMPFIRE, FURNACE_MINECART, FURNACE, SMOKER -> this.checkIsland(e, player, loc, Flags.FURNACE);
case ENCHANTING_TABLE -> this.checkIsland(e, player, loc, Flags.ENCHANTING);
case ENDER_CHEST -> this.checkIsland(e, player, loc, Flags.ENDER_CHEST);
case JUKEBOX -> this.checkIsland(e, player, loc, Flags.JUKEBOX);
case NOTE_BLOCK -> this.checkIsland(e, player, loc, Flags.NOTE_BLOCK);
case CRAFTING_TABLE, CARTOGRAPHY_TABLE, GRINDSTONE, STONECUTTER, LOOM -> this.checkIsland(e, player, loc, Flags.CRAFTING);
case LEVER -> this.checkIsland(e, player, loc, Flags.LEVER);
case REDSTONE_WIRE, REPEATER, COMPARATOR, DAYLIGHT_DETECTOR -> this.checkIsland(e, player, loc, Flags.REDSTONE);
case DRAGON_EGG -> this.checkIsland(e, player, loc, Flags.DRAGON_EGG);
case END_PORTAL_FRAME, RESPAWN_ANCHOR -> this.checkIsland(e, player, loc, Flags.PLACE_BLOCKS);
case GLOW_ITEM_FRAME, ITEM_FRAME -> this.checkIsland(e, player, loc, Flags.ITEM_FRAME);
case SWEET_BERRY_BUSH, CAVE_VINES -> this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS);
case CAKE -> this.checkIsland(e, player, loc, Flags.CAKE);
case CHISELED_BOOKSHELF -> this.checkIsland(e, player, loc, Flags.BOOKSHELF);
case LAVA_CAULDRON ->
{
if (BlockInteractionListener.holds(player, Material.BUCKET))
{
this.checkIsland(e, player, loc, Flags.COLLECT_LAVA);
}
}
case WATER_CAULDRON ->
{
if (BlockInteractionListener.holds(player, Material.BUCKET))
{
this.checkIsland(e, player, loc, Flags.COLLECT_WATER);
}
else if (BlockInteractionListener.holds(player, Material.GLASS_BOTTLE) ||
BlockInteractionListener.holds(player, Material.POTION))
{
this.checkIsland(e, player, loc, Flags.BREWING);
}
}
case POWDER_SNOW_CAULDRON ->
{
if (BlockInteractionListener.holds(player, Material.BUCKET))
{
this.checkIsland(e, player, loc, Flags.COLLECT_POWDERED_SNOW);
}
}
case CAULDRON ->
{
if (BlockInteractionListener.holds(player, Material.WATER_BUCKET) ||
BlockInteractionListener.holds(player, Material.LAVA_BUCKET) ||
BlockInteractionListener.holds(player, Material.POWDER_SNOW_BUCKET))
{
this.checkIsland(e, player, loc, Flags.BUCKET);
}
else if (BlockInteractionListener.holds(player, Material.POTION))
{
this.checkIsland(e, player, loc, Flags.BREWING);
}
}
default ->
{ // nothing to do
}
}
}
private boolean checkTags(Event e, Player player, Block block) {
Material type = block.getType();
Location loc = block.getLocation();
if (Tag.ANVIL.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.ANVIL);
return true;
}
if (Tag.BUTTONS.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.BUTTON);
return true;
}
if (Tag.BEDS.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.BED);
return true;
}
if (Tag.DOORS.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.DOOR);
return true;
}
if (Tag.SHULKER_BOXES.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.SHULKER_BOX);
return true;
}
if (Tag.TRAPDOORS.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.TRAPDOOR);
return true;
}
if (Tag.SIGNS.isTagged(type) && block.getState() instanceof Sign sign && !sign.isWaxed()) {
// If waxed, then sign cannot be edited otherwise check
this.checkIsland(e, player, loc, Flags.SIGN_EDITING);
return true;
}
if (Tag.FENCE_GATES.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.GATE);
return true;
}
if (Tag.ITEMS_CHEST_BOATS.isTagged(type))
{
this.checkIsland(e, player, loc, Flags.CHEST);
return true;
}
return false;
}
private boolean checkSpecialCases(Event e, Player player, Block block) {
Material type = block.getType();
Location loc = block.getLocation();
// Handle pots
if (type.name().startsWith("POTTED"))
{
this.checkIsland(e, player, loc, Flags.FLOWER_POT);
return true;
}
if (block.getState() instanceof BrushableBlock && BlockInteractionListener.holds(player, Material.BRUSH)) {
// Protect this using break blocks flag for now. Maybe in the future it can have its own flag.
this.checkIsland(e, player, loc, Flags.BREAK_BLOCKS);
return true;
}
return false;
}
/**
* When breaking blocks is allowed, this protects specific blocks from being broken, which would bypass the
* protection. For example, player enables break blocks, but chests are still protected Fires after the BreakBlocks
* check.
*
* @param e - event
*/
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlockBreak(final BlockBreakEvent e)
{
this.checkClickedBlock(e, e.getPlayer(), e.getBlock());
}
/**
* Prevents dragon eggs from flying out of an island's protected space
*
* @param e - event
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onDragonEggTeleport(BlockFromToEvent e)
{
Block block = e.getBlock();
if (!block.getType().equals(Material.DRAGON_EGG) || !this.getIWM().inWorld(block.getLocation()))
{
return;
}
// If egg starts in a protected island...
// Cancel if toIsland is not fromIsland or if there is no protected island there
// This protects against eggs dropping into adjacent islands, e.g. island distance and protection range are equal
Optional<Island> fromIsland = this.getIslands().getProtectedIslandAt(block.getLocation());
Optional<Island> toIsland = this.getIslands().getProtectedIslandAt(e.getToBlock().getLocation());
fromIsland.ifPresent(from -> e.setCancelled(toIsland.map(to -> to != from).orElse(true)));
}
/**
* This method returns if player is holding given material in main or offhand.
* @param player Player that must be checked.
* @param material item that mus t be checjed.
* @return {@code true} if player is holding item in main hand or offhand.
*/
private static boolean holds(Player player, Material material)
{
return player.getInventory().getItemInMainHand().getType().equals(material) ||
player.getInventory().getItemInOffHand().getType().equals(material);
}
}