opMob = is.getNearestMob(addon.getSettings().getMobWarning());
- opMob.stream().filter(MOB_ASPECTS::containsKey).map(MOB_ASPECTS::get).forEach(s -> {
- block.getWorld().playSound(block.getLocation(), s.sound(), 1F, 1F);
- block.getWorld().spawnParticle(Particle.REDSTONE, block.getLocation().add(new Vector(0.5, 1.0, 0.5)), 10, 0.5, 0, 0.5, 1, new Particle.DustOptions(s.color(), 1));
- });
-
- }
-
- /**
- * Check whether this phase is done or not.
- *
- * @param player - player
- * @param i - island
- * @param is - OneBlockIslands object
- * @param phase - current phase name
- * @return true if this is a new phase, false if not
- */
- protected boolean checkPhase(@Nullable Player player, @NonNull Island i, @NonNull OneBlockIslands is, @NonNull OneBlockPhase phase) {
- // Handle NPCs
- User user;
- if (player == null || player.hasMetadata("NPC")) {
- // Default to the owner
- user = addon.getPlayers().getUser(i.getOwner());
- } else {
- user = User.getInstance(player);
- }
-
- String phaseName = phase.getPhaseName() == null ? "" : phase.getPhaseName();
- if (!is.getPhaseName().equalsIgnoreCase(phaseName)) {
- // Run previous phase end commands
- oneBlocksManager.getPhase(is.getPhaseName()).ifPresent(oldPhase -> {
- String oldPhaseName = oldPhase.getPhaseName() == null ? "" : oldPhase.getPhaseName();
- Util.runCommands(user,
- replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getEndCommands()),
- "Commands run for end of " + oldPhaseName);
- // If first time
- if (is.getBlockNumber() >= is.getLifetime()) {
- Util.runCommands(user,
- replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getFirstTimeEndCommands()),
- "Commands run for first time completing " + oldPhaseName);
- }
- });
- // Set the phase name
- is.setPhaseName(phaseName);
- if (user.isPlayer() && user.isOnline() && addon.inWorld(user.getWorld())) {
- user.getPlayer().sendTitle(phaseName, null, -1, -1, -1);
- }
- // Run phase start commands
- Util.runCommands(user,
- replacePlaceholders(player, phaseName, phase.getBlockNumber(), i, phase.getStartCommands()),
- "Commands run for start of " + phaseName);
-
- saveIsland(i);
- return true;
- }
- return false;
- }
-
- /**
- * Replaces placeholders in commands.
- *
- * [island] - Island name
- * [owner] - Island owner's name
- * [player] - The name of the player who broke the block triggering the commands
- * [phase] - the name of this phase
- * [blocks] - the number of blocks broken
- * [level] - island level (Requires Levels Addon)
- * [bank-balance] - island bank balance (Requires Bank Addon)
- * [eco-balance] - player's economy balance (Requires Vault and an economy plugin)
- *
- *
- * @param player - player
- * @param phaseName - phase name
- * @param phaseNumber - phase's block number
- * @param i - island
- * @param commands - list of commands
- * @return list of commands with placeholders replaced
- */
- @NonNull
- List replacePlaceholders(@Nullable Player player, @NonNull String phaseName, @NonNull String phaseNumber, @NonNull Island i, List commands) {
- return commands.stream()
- .map(c -> {
- long level = addon.getAddonByName("Level").map(l -> ((Level) l).getIslandLevel(addon.getOverWorld(), i.getOwner())).orElse(0L);
- double balance = addon.getAddonByName("Bank").map(b -> ((Bank) b).getBankManager().getBalance(i).getValue()).orElse(0D);
- double ecoBalance = addon.getPlugin().getVault().map(v -> v.getBalance(User.getInstance(player), addon.getOverWorld())).orElse(0D);
-
- return c.replace("[island]", i.getName() == null ? "" : i.getName())
- .replace("[owner]", addon.getPlayers().getName(i.getOwner()))
- .replace("[phase]", phaseName)
- .replace("[blocks]", phaseNumber)
- .replace("[level]", String.valueOf(level))
- .replace("[bank-balance]", String.valueOf(balance))
- .replace("[eco-balance]", String.valueOf(ecoBalance));
-
- })
- .map(c -> addon.getPlugin().getPlaceholdersManager().replacePlaceholders(player, c))
- .collect(Collectors.toList());
+ private void handleGoto(OneBlockIslands is, OneBlockPhase phase) {
+ int gotoBlock = phase.getGotoBlock();
+ phase = oneBlocksManager.getPhase(gotoBlock);
+ // Store lifetime
+ is.setLifetime(is.getLifetime() + gotoBlock);
+ // Set current block
+ is.setBlockNumber(gotoBlock);
}
private void setBiome(@NonNull Block block, @Nullable Biome biome) {
@@ -651,11 +425,27 @@ private void spawnBlock(@NonNull OneBlockObject nextBlock, @NonNull Block block)
if (type.equals(Material.CHEST) && nextBlock.getChest() != null) {
fillChest(nextBlock, block);
return;
- }
- if (Tag.LEAVES.isTagged(type)) {
+ } else if (Tag.LEAVES.isTagged(type)) {
Leaves leaves = (Leaves) block.getState().getBlockData();
leaves.setPersistent(true);
block.setBlockData(leaves);
+ } else if (block.getState() instanceof BrushableBlock bb) {
+ LootTable lt = switch(bb.getBlock().getBiome()) {
+ default -> {
+ if (random.nextDouble() < 0.8) {
+ yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_COMMON.getLootTable();
+ } else {
+ // 20% rare
+ yield LootTables.TRAIL_RUINS_ARCHAEOLOGY_RARE.getLootTable();
+ }
+ }
+ case DESERT -> LootTables.DESERT_PYRAMID_ARCHAEOLOGY.getLootTable();
+ case FROZEN_OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable();
+ case OCEAN -> LootTables.OCEAN_RUIN_COLD_ARCHAEOLOGY.getLootTable();
+ case WARM_OCEAN -> LootTables.OCEAN_RUIN_WARM_ARCHAEOLOGY.getLootTable();
+ };
+ bb.setLootTable(lt);
+ bb.update();
}
}
@@ -665,163 +455,12 @@ private void spawnEntity(@NonNull OneBlockObject nextBlock, @NonNull Block block
Entity entity = block.getWorld().spawnEntity(spawnLoc, nextBlock.getEntityType());
// Make space for entity - this will blot out blocks
if (addon.getSettings().isClearBlocks()) {
- makeSpace(entity, spawnLoc);
+ new MakeSpace(addon).makeSpace(entity, spawnLoc);
}
block.getWorld().playSound(block.getLocation(), Sound.ENTITY_ENDERMAN_TELEPORT, 1F, 2F);
}
- /**
- * This method creates a space for entities to spawn. Avoids block damage.
- * @param entity Entity that is spawned.
- * @param spawnLocation Location where entity is spawned.
- */
- private void makeSpace(@NonNull Entity entity, @NonNull Location spawnLocation)
- {
- World world = entity.getWorld();
- List airBlocks = new ArrayList<>();
- List waterBlocks = new ArrayList<>();
- // Make space for entity based on the entity's size
- final BoundingBox boundingBox = entity.getBoundingBox();
- final boolean isWaterProtected = this.addon.getSettings().isWaterMobProtection() &&
- WATER_ENTITIES.contains(entity.getType());
-
- for (double y = boundingBox.getMinY(); y <= Math.min(boundingBox.getMaxY(), world.getMaxHeight()); y++)
- {
- // Start with middle block.
- Block block = world.getBlockAt(new Location(world, spawnLocation.getBlockX(), y, spawnLocation.getBlockZ()));
-
- // Check if block must be replaced with air or water.
- this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
-
- // If entity requires water protection, then add air block above it. Dolphin protection.
- if (isWaterProtected)
- {
- // Look up only if possible
- if (y + 1 < world.getMaxHeight())
- {
- airBlocks.add(block.getRelative(BlockFace.UP));
- }
- }
-
- // Process entity width and depth.
- if (boundingBox.getWidthX() > 1 && boundingBox.getWidthZ() > 1)
- {
- // Entities are spawned in the middle of block. So add extra half block to both dimensions.
- for (double x = boundingBox.getMinX() - 0.5; x < boundingBox.getMaxX() + 0.5; x++)
- {
- for (double z = boundingBox.getMinZ() - 0.5; z < boundingBox.getMaxZ() + 0.5; z++)
- {
- block = world.getBlockAt(new Location(world,
- x,
- y,
- z));
-
- // Check if block should be marked.
- this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
- }
- }
- }
- else if (boundingBox.getWidthX() > 1)
- {
- // If entity is just wider, then check the one dimension.
- for (double x = boundingBox.getMinX() - 0.5; x < boundingBox.getMaxX() + 0.5; x++)
- {
- block = world.getBlockAt(new Location(world,
- x,
- y,
- spawnLocation.getZ()));
-
- // Check if block should be marked.
- this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
- }
- }
- else if (boundingBox.getWidthZ() > 1)
- {
- // If entity is just wider, then check the one dimension.
- for (double z = boundingBox.getMinZ() - 0.5; z < boundingBox.getMaxZ() + 0.5; z++)
- {
- block = world.getBlockAt(new Location(world,
- spawnLocation.getX(),
- y,
- z));
-
- // Check if block should be marked.
- this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
- }
- }
- }
-
- // Fire event
- BlockClearEvent event = new BlockClearEvent(entity, airBlocks, waterBlocks);
- Bukkit.getPluginManager().callEvent(event);
-
- if (event.isCancelled())
- {
- // Event is cancelled. Blocks cannot be removed.
- return;
- }
-
- // Break blocks.
- airBlocks.forEach(Block::breakNaturally);
- airBlocks.forEach(b -> b.setType(Material.AIR));
- waterBlocks.forEach(block -> {
- if (block.getBlockData() instanceof Waterlogged waterlogged)
- {
- // If block was not removed and is waterlogged, then it means it can be just waterlogged.
- waterlogged.setWaterlogged(true);
- }
- else
- {
- // Replace block with water.
- block.setType(Material.WATER);
- }
- });
- }
-
-
- /**
- * This method checks if block bounding box overlaps with entity bounding box and populates lists accordingly.
- * @param block Block that need to be checked.
- * @param boundingBox The bounding box of entity.
- * @param isWaterEntity Boolean that indicate that entity must be water-protected.
- * @param airBlocks List of air blocks.
- * @param waterBlocks List of water blocks.
- */
- private void checkBlock(Block block,
- BoundingBox boundingBox,
- boolean isWaterEntity,
- List airBlocks,
- List waterBlocks)
- {
- // Check if block should be marked for destroying.
- if (block.getBoundingBox().overlaps(boundingBox))
- {
- // Only if entity collides with the block.
- airBlocks.add(block);
- }
-
- // Check if block should be marked for replacing with water.
- if (isWaterEntity)
- {
- if (block.getBlockData() instanceof Waterlogged waterlogged)
- {
- // Check if waterlogged block collides.
- if (block.getBoundingBox().overlaps(boundingBox) || !waterlogged.isWaterlogged())
- {
- // if block overlaps with entity, it will be replaced with air.
- // if block is not waterlogged, put water in it.
- waterBlocks.add(block);
- }
- }
- else if (block.getType() != Material.WATER)
- {
- // Well, unfortunately block must go.
- waterBlocks.add(block);
- }
- }
- }
-
private void fillChest(@NonNull OneBlockObject nextBlock, @NonNull Block block) {
Chest chest = (Chest) block.getState();
diff --git a/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java b/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java
new file mode 100644
index 0000000..d5e4f85
--- /dev/null
+++ b/src/main/java/world/bentobox/aoneblock/listeners/CheckPhase.java
@@ -0,0 +1,190 @@
+package world.bentobox.aoneblock.listeners;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+import world.bentobox.aoneblock.AOneBlock;
+import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
+import world.bentobox.aoneblock.oneblocks.OneBlockPhase;
+import world.bentobox.aoneblock.oneblocks.OneBlocksManager;
+import world.bentobox.aoneblock.oneblocks.Requirement;
+import world.bentobox.bank.Bank;
+import world.bentobox.bentobox.api.localization.TextVariables;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.database.objects.Island;
+import world.bentobox.bentobox.util.Util;
+import world.bentobox.level.Level;
+
+/**
+ * Performs end of phase checking
+ * @author tastybento
+ *
+ */
+public class CheckPhase {
+
+ private final AOneBlock addon;
+ private final OneBlocksManager oneBlocksManager;
+ private final BlockListener blockListener;
+
+
+ /**
+ * @param addon AOneBlock
+ * @param blockListener
+ */
+ public CheckPhase(AOneBlock addon, BlockListener blockListener) {
+ this.addon = addon;
+ this.oneBlocksManager = addon.getOneBlockManager();
+ this.blockListener = blockListener;
+
+ }
+
+ /**
+ * Checks whether the player can proceed to the next phase
+ *
+ * @param player - player
+ * @param i - island
+ * @param phase - one block phase
+ * @param world - world
+ * @return true if the player can proceed to the next phase, false if not or if there is no next phase.
+ */
+ protected boolean phaseRequirementsFail(@Nullable Player player, @NonNull Island i, OneBlockPhase phase, @NonNull World world) {
+ if (phase.getRequirements().isEmpty()) {
+ return false;
+ }
+ // Check requirements
+ for (Requirement r : phase.getRequirements()) {
+ switch (r.getType()) {
+ case LEVEL:
+ return addon.getAddonByName("Level").map(l -> {
+ if (((Level) l).getIslandLevel(world, i.getOwner()) < r.getLevel()) {
+ User.getInstance(player).sendMessage("aoneblock.phase.insufficient-level", TextVariables.NUMBER, String.valueOf(r.getLevel()));
+ return true;
+ }
+ return false;
+ }).orElse(false);
+ case BANK:
+ return addon.getAddonByName("Bank").map(l -> {
+ if (((Bank) l).getBankManager().getBalance(i).getValue() < r.getBank()) {
+ User.getInstance(player).sendMessage("aoneblock.phase.insufficient-bank-balance", TextVariables.NUMBER, String.valueOf(r.getBank()));
+ return true;
+ }
+ return false;
+ }).orElse(false);
+ case ECO:
+ return addon.getPlugin().getVault().map(l -> {
+ if (l.getBalance(User.getInstance(player), world) < r.getEco()) {
+ User.getInstance(player).sendMessage("aoneblock.phase.insufficient-funds", TextVariables.NUMBER, String.valueOf(r.getEco()));
+ return true;
+ }
+ return false;
+ }).orElse(false);
+ case PERMISSION:
+ if (player != null && !player.hasPermission(r.getPermission())) {
+ User.getInstance(player).sendMessage("aoneblock.phase.insufficient-permission", TextVariables.NAME, String.valueOf(r.getPermission()));
+ return true;
+ }
+ return false;
+ default:
+ break;
+
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check whether this phase is done or not.
+ *
+ * @param player - player
+ * @param i - island
+ * @param is - OneBlockIslands object
+ * @param phase - current phase name
+ * @return true if this is a new phase, false if not
+ */
+ protected boolean checkPhase(@Nullable Player player, @NonNull Island i, @NonNull OneBlockIslands is, @NonNull OneBlockPhase phase) {
+ // Handle NPCs
+ User user;
+ if (player == null || player.hasMetadata("NPC")) {
+ // Default to the owner
+ user = addon.getPlayers().getUser(i.getOwner());
+ } else {
+ user = User.getInstance(player);
+ }
+
+ String phaseName = phase.getPhaseName() == null ? "" : phase.getPhaseName();
+ if (!is.getPhaseName().equalsIgnoreCase(phaseName)) {
+ // Run previous phase end commands
+ oneBlocksManager.getPhase(is.getPhaseName()).ifPresent(oldPhase -> {
+ String oldPhaseName = oldPhase.getPhaseName() == null ? "" : oldPhase.getPhaseName();
+ Util.runCommands(user,
+ replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getEndCommands()),
+ "Commands run for end of " + oldPhaseName);
+ // If first time
+ if (is.getBlockNumber() >= is.getLifetime()) {
+ Util.runCommands(user,
+ replacePlaceholders(player, oldPhaseName, phase.getBlockNumber(), i, oldPhase.getFirstTimeEndCommands()),
+ "Commands run for first time completing " + oldPhaseName);
+ }
+ });
+ // Set the phase name
+ is.setPhaseName(phaseName);
+ if (user.isPlayer() && user.isOnline() && addon.inWorld(user.getWorld())) {
+ user.getPlayer().sendTitle(phaseName, null, -1, -1, -1);
+ }
+ // Run phase start commands
+ Util.runCommands(user,
+ replacePlaceholders(player, phaseName, phase.getBlockNumber(), i, phase.getStartCommands()),
+ "Commands run for start of " + phaseName);
+
+ blockListener.saveIsland(i);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Replaces placeholders in commands.
+ *
+ * [island] - Island name
+ * [owner] - Island owner's name
+ * [player] - The name of the player who broke the block triggering the commands
+ * [phase] - the name of this phase
+ * [blocks] - the number of blocks broken
+ * [level] - island level (Requires Levels Addon)
+ * [bank-balance] - island bank balance (Requires Bank Addon)
+ * [eco-balance] - player's economy balance (Requires Vault and an economy plugin)
+ *
+ *
+ * @param player - player
+ * @param phaseName - phase name
+ * @param phaseNumber - phase's block number
+ * @param i - island
+ * @param commands - list of commands
+ * @return list of commands with placeholders replaced
+ */
+ @NonNull
+ List replacePlaceholders(@Nullable Player player, @NonNull String phaseName, @NonNull String phaseNumber, @NonNull Island i, List commands) {
+ return commands.stream()
+ .map(c -> {
+ long level = addon.getAddonByName("Level").map(l -> ((Level) l).getIslandLevel(addon.getOverWorld(), i.getOwner())).orElse(0L);
+ double balance = addon.getAddonByName("Bank").map(b -> ((Bank) b).getBankManager().getBalance(i).getValue()).orElse(0D);
+ double ecoBalance = addon.getPlugin().getVault().map(v -> v.getBalance(User.getInstance(player), addon.getOverWorld())).orElse(0D);
+
+ return c.replace("[island]", i.getName() == null ? "" : i.getName())
+ .replace("[owner]", addon.getPlayers().getName(i.getOwner()))
+ .replace("[phase]", phaseName)
+ .replace("[blocks]", phaseNumber)
+ .replace("[level]", String.valueOf(level))
+ .replace("[bank-balance]", String.valueOf(balance))
+ .replace("[eco-balance]", String.valueOf(ecoBalance));
+
+ })
+ .map(c -> addon.getPlugin().getPlaceholdersManager().replacePlaceholders(player, c))
+ .collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/world/bentobox/aoneblock/listeners/MakeSpace.java b/src/main/java/world/bentobox/aoneblock/listeners/MakeSpace.java
new file mode 100644
index 0000000..75d2dce
--- /dev/null
+++ b/src/main/java/world/bentobox/aoneblock/listeners/MakeSpace.java
@@ -0,0 +1,199 @@
+package world.bentobox.aoneblock.listeners;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.block.data.Waterlogged;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.util.BoundingBox;
+import org.eclipse.jdt.annotation.NonNull;
+
+import world.bentobox.aoneblock.AOneBlock;
+import world.bentobox.aoneblock.events.BlockClearEvent;
+
+/**
+ * This class creates a space for entities to spawn. Avoids block damage.
+ * @author tastybento
+ *
+ */
+public class MakeSpace {
+
+ /**
+ * Water entities
+ */
+ static final List WATER_ENTITIES = List.of(
+ EntityType.GUARDIAN, EntityType.ELDER_GUARDIAN, EntityType.COD, EntityType.SALMON, EntityType.PUFFERFISH,
+ EntityType.TROPICAL_FISH, EntityType.DROWNED, EntityType.DOLPHIN, EntityType.TADPOLE,
+ EntityType.SQUID, EntityType.AXOLOTL, EntityType.GLOW_SQUID);
+
+ /**
+ * Main addon class.
+ */
+ private final AOneBlock addon;
+
+
+ /**
+ * @param addon
+ */
+ public MakeSpace(AOneBlock addon) {
+ this.addon = addon;
+ }
+
+
+ /**
+ * This method creates a space for entities to spawn. Avoids block damage.
+ * @param entity Entity that is spawned.
+ * @param spawnLocation Location where entity is spawned.
+ */
+ void makeSpace(@NonNull Entity entity, @NonNull Location spawnLocation)
+ {
+ World world = entity.getWorld();
+ List airBlocks = new ArrayList<>();
+ List waterBlocks = new ArrayList<>();
+ // Make space for entity based on the entity's size
+ final BoundingBox boundingBox = entity.getBoundingBox();
+ final boolean isWaterProtected = this.addon.getSettings().isWaterMobProtection() &&
+ WATER_ENTITIES.contains(entity.getType());
+
+ for (double y = boundingBox.getMinY(); y <= Math.min(boundingBox.getMaxY(), world.getMaxHeight()); y++)
+ {
+ // Start with middle block.
+ Block block = world.getBlockAt(new Location(world, spawnLocation.getBlockX(), y, spawnLocation.getBlockZ()));
+
+ // Check if block must be replaced with air or water.
+ this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
+
+ // If entity requires water protection, then add air block above it. Dolphin protection.
+ if (isWaterProtected)
+ {
+ // Look up only if possible
+ if (y + 1 < world.getMaxHeight())
+ {
+ airBlocks.add(block.getRelative(BlockFace.UP));
+ }
+ }
+
+ // Process entity width and depth.
+ if (boundingBox.getWidthX() > 1 && boundingBox.getWidthZ() > 1)
+ {
+ // Entities are spawned in the middle of block. So add extra half block to both dimensions.
+ for (double x = boundingBox.getMinX() - 0.5; x < boundingBox.getMaxX() + 0.5; x++)
+ {
+ for (double z = boundingBox.getMinZ() - 0.5; z < boundingBox.getMaxZ() + 0.5; z++)
+ {
+ block = world.getBlockAt(new Location(world,
+ x,
+ y,
+ z));
+
+ // Check if block should be marked.
+ this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
+ }
+ }
+ }
+ else if (boundingBox.getWidthX() > 1)
+ {
+ // If entity is just wider, then check the one dimension.
+ for (double x = boundingBox.getMinX() - 0.5; x < boundingBox.getMaxX() + 0.5; x++)
+ {
+ block = world.getBlockAt(new Location(world,
+ x,
+ y,
+ spawnLocation.getZ()));
+
+ // Check if block should be marked.
+ this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
+ }
+ }
+ else if (boundingBox.getWidthZ() > 1)
+ {
+ // If entity is just wider, then check the one dimension.
+ for (double z = boundingBox.getMinZ() - 0.5; z < boundingBox.getMaxZ() + 0.5; z++)
+ {
+ block = world.getBlockAt(new Location(world,
+ spawnLocation.getX(),
+ y,
+ z));
+
+ // Check if block should be marked.
+ this.checkBlock(block, boundingBox, isWaterProtected, airBlocks, waterBlocks);
+ }
+ }
+ }
+
+ // Fire event
+ BlockClearEvent event = new BlockClearEvent(entity, airBlocks, waterBlocks);
+ Bukkit.getPluginManager().callEvent(event);
+
+ if (event.isCancelled())
+ {
+ // Event is cancelled. Blocks cannot be removed.
+ return;
+ }
+
+ // Break blocks.
+ airBlocks.forEach(Block::breakNaturally);
+ airBlocks.forEach(b -> b.setType(Material.AIR));
+ waterBlocks.forEach(block -> {
+ if (block.getBlockData() instanceof Waterlogged waterlogged)
+ {
+ // If block was not removed and is waterlogged, then it means it can be just waterlogged.
+ waterlogged.setWaterlogged(true);
+ }
+ else
+ {
+ // Replace block with water.
+ block.setType(Material.WATER);
+ }
+ });
+ }
+
+ /**
+ * This method checks if block bounding box overlaps with entity bounding box and populates lists accordingly.
+ * @param block Block that need to be checked.
+ * @param boundingBox The bounding box of entity.
+ * @param isWaterEntity Boolean that indicate that entity must be water-protected.
+ * @param airBlocks List of air blocks.
+ * @param waterBlocks List of water blocks.
+ */
+ private void checkBlock(Block block,
+ BoundingBox boundingBox,
+ boolean isWaterEntity,
+ List airBlocks,
+ List waterBlocks)
+ {
+ // Check if block should be marked for destroying.
+ if (block.getBoundingBox().overlaps(boundingBox))
+ {
+ // Only if entity collides with the block.
+ airBlocks.add(block);
+ }
+
+ // Check if block should be marked for replacing with water.
+ if (isWaterEntity)
+ {
+ if (block.getBlockData() instanceof Waterlogged waterlogged)
+ {
+ // Check if waterlogged block collides.
+ if (block.getBoundingBox().overlaps(boundingBox) || !waterlogged.isWaterlogged())
+ {
+ // if block overlaps with entity, it will be replaced with air.
+ // if block is not waterlogged, put water in it.
+ waterBlocks.add(block);
+ }
+ }
+ else if (block.getType() != Material.WATER)
+ {
+ // Well, unfortunately block must go.
+ waterBlocks.add(block);
+ }
+ }
+ }
+}
diff --git a/src/main/java/world/bentobox/aoneblock/listeners/WarningSounder.java b/src/main/java/world/bentobox/aoneblock/listeners/WarningSounder.java
new file mode 100644
index 0000000..7e98258
--- /dev/null
+++ b/src/main/java/world/bentobox/aoneblock/listeners/WarningSounder.java
@@ -0,0 +1,86 @@
+package world.bentobox.aoneblock.listeners;
+
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import org.bukkit.Color;
+import org.bukkit.Particle;
+import org.bukkit.Sound;
+import org.bukkit.block.Block;
+import org.bukkit.entity.EntityType;
+import org.bukkit.util.Vector;
+import org.eclipse.jdt.annotation.NonNull;
+
+import world.bentobox.aoneblock.AOneBlock;
+import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
+import world.bentobox.aoneblock.oneblocks.MobAspects;
+
+/**
+ * Plays a warning sound
+ * @author tastybento
+ *
+ */
+public class WarningSounder {
+
+ private final AOneBlock addon;
+
+ /**
+ * @param addon
+ */
+ public WarningSounder(AOneBlock addon) {
+ this.addon = addon;
+ }
+ /**
+ * Mob aspects.
+ */
+ private static final Map MOB_ASPECTS;
+
+ static {
+ Map m = new EnumMap<>(EntityType.class);
+ m.put(EntityType.BLAZE, new MobAspects(Sound.ENTITY_BLAZE_AMBIENT, Color.fromRGB(238, 211, 91)));
+ m.put(EntityType.CAVE_SPIDER, new MobAspects(Sound.ENTITY_SPIDER_AMBIENT, Color.fromRGB(63, 37, 31)));
+ m.put(EntityType.CREEPER, new MobAspects(Sound.ENTITY_CREEPER_PRIMED, Color.fromRGB(125, 255, 106)));
+ m.put(EntityType.DROWNED, new MobAspects(Sound.ENTITY_DROWNED_AMBIENT, Color.fromRGB(109, 152, 144)));
+ m.put(EntityType.ELDER_GUARDIAN, new MobAspects(Sound.ENTITY_ELDER_GUARDIAN_AMBIENT, Color.fromRGB(201, 143, 113)));
+ m.put(EntityType.ENDERMAN, new MobAspects(Sound.ENTITY_ENDERMAN_AMBIENT, Color.fromRGB(0, 0, 0)));
+ m.put(EntityType.ENDERMITE, new MobAspects(Sound.ENTITY_ENDERMITE_AMBIENT, Color.fromRGB(30, 30, 30)));
+ m.put(EntityType.EVOKER, new MobAspects(Sound.ENTITY_EVOKER_AMBIENT, Color.fromRGB(144, 148, 148)));
+ m.put(EntityType.GHAST, new MobAspects(Sound.ENTITY_GHAST_AMBIENT, Color.fromRGB(242, 242, 242)));
+ m.put(EntityType.GUARDIAN, new MobAspects(Sound.ENTITY_GUARDIAN_AMBIENT, Color.fromRGB(201, 143, 113)));
+ m.put(EntityType.HUSK, new MobAspects(Sound.ENTITY_HUSK_AMBIENT, Color.fromRGB(111, 104, 90)));
+ m.put(EntityType.ILLUSIONER, new MobAspects(Sound.ENTITY_ILLUSIONER_AMBIENT, Color.fromRGB(144, 149, 149)));
+ m.put(EntityType.PIGLIN, new MobAspects(Sound.ENTITY_PIGLIN_AMBIENT, Color.fromRGB(255, 215, 0)));
+ m.put(EntityType.PIGLIN_BRUTE, new MobAspects(Sound.ENTITY_PIGLIN_BRUTE_AMBIENT, Color.fromRGB(255, 215, 0)));
+ m.put(EntityType.ZOMBIFIED_PIGLIN, new MobAspects(Sound.ENTITY_ZOMBIFIED_PIGLIN_AMBIENT, Color.fromRGB(125, 100, 0)));
+ m.put(EntityType.PILLAGER, new MobAspects(Sound.ENTITY_PILLAGER_AMBIENT, Color.fromRGB(74, 74, 53)));
+ m.put(EntityType.RAVAGER, new MobAspects(Sound.ENTITY_RAVAGER_AMBIENT, Color.fromRGB(85, 78, 73)));
+ m.put(EntityType.SHULKER, new MobAspects(Sound.ENTITY_SHULKER_AMBIENT, Color.fromRGB(142, 106, 146)));
+ m.put(EntityType.SILVERFISH, new MobAspects(Sound.ENTITY_SILVERFISH_AMBIENT, Color.fromRGB(211, 211, 211)));
+ m.put(EntityType.SKELETON, new MobAspects(Sound.ENTITY_SKELETON_AMBIENT, Color.fromRGB(211, 211, 211)));
+ m.put(EntityType.SPIDER, new MobAspects(Sound.ENTITY_SPIDER_AMBIENT, Color.fromRGB(94, 84, 73)));
+ m.put(EntityType.STRAY, new MobAspects(Sound.ENTITY_STRAY_AMBIENT, Color.fromRGB(118, 132, 135)));
+ m.put(EntityType.VEX, new MobAspects(Sound.ENTITY_VEX_AMBIENT, Color.fromRGB(137, 156, 176)));
+ m.put(EntityType.VINDICATOR, new MobAspects(Sound.ENTITY_VINDICATOR_AMBIENT, Color.fromRGB(137, 156, 166)));
+ m.put(EntityType.WITCH, new MobAspects(Sound.ENTITY_WITCH_AMBIENT, Color.fromRGB(56, 39, 67)));
+ m.put(EntityType.WITHER, new MobAspects(Sound.ENTITY_WITHER_AMBIENT, Color.fromRGB(80, 80, 80)));
+ m.put(EntityType.WARDEN, new MobAspects(Sound.ENTITY_WARDEN_AMBIENT, Color.fromRGB(6, 72, 86))); //ADDED WARDEN SOUND
+ m.put(EntityType.WITHER_SKELETON, new MobAspects(Sound.ENTITY_WITHER_SKELETON_AMBIENT, Color.fromRGB(100, 100, 100)));
+ m.put(EntityType.ZOGLIN, new MobAspects(Sound.ENTITY_ZOGLIN_AMBIENT, Color.fromRGB(255, 192, 203)));
+ m.put(EntityType.ZOMBIE, new MobAspects(Sound.ENTITY_ZOMBIE_AMBIENT, Color.fromRGB(74, 99, 53)));
+ m.put(EntityType.ZOMBIE_VILLAGER, new MobAspects(Sound.ENTITY_ZOMBIE_VILLAGER_AMBIENT, Color.fromRGB(111, 104, 90)));
+
+ MOB_ASPECTS = Collections.unmodifiableMap(m);
+ }
+
+ void play(@NonNull OneBlockIslands is, @NonNull Block block) {
+ List opMob = is.getNearestMob(addon.getSettings().getMobWarning());
+ opMob.stream().filter(MOB_ASPECTS::containsKey).map(MOB_ASPECTS::get).forEach(s -> {
+ block.getWorld().playSound(block.getLocation(), s.sound(), 1F, 1F);
+ block.getWorld().spawnParticle(Particle.REDSTONE, block.getLocation().add(new Vector(0.5, 1.0, 0.5)), 10, 0.5, 0, 0.5, 1, new Particle.DustOptions(s.color(), 1));
+ });
+
+ }
+
+}
diff --git a/src/main/resources/phases/0_plains.yml b/src/main/resources/phases/0_plains.yml
index ad39dde..588a801 100644
--- a/src/main/resources/phases/0_plains.yml
+++ b/src/main/resources/phases/0_plains.yml
@@ -87,6 +87,7 @@
CLAY: 40
EMERALD_ORE: 10
DIRT_PATH: 100
+ COPPER_ORE: 200
mobs:
COW: 150
SPIDER: 75
diff --git a/src/main/resources/phases/3000_ocean.yml b/src/main/resources/phases/3000_ocean.yml
index d02187b..4765d52 100644
--- a/src/main/resources/phases/3000_ocean.yml
+++ b/src/main/resources/phases/3000_ocean.yml
@@ -27,6 +27,8 @@
DARK_PRISMARINE: 100
DEAD_BRAIN_CORAL_BLOCK: 10
SEA_LANTERN: 100
+ SUSPICIOUS_SAND: 100
+ SUSPICIOUS_GRAVEL: 100
mobs:
COD: 100
TROPICAL_FISH: 100
diff --git a/src/main/resources/phases/4000_jungle.yml b/src/main/resources/phases/4000_jungle.yml
index 1154475..0584dd6 100644
--- a/src/main/resources/phases/4000_jungle.yml
+++ b/src/main/resources/phases/4000_jungle.yml
@@ -22,6 +22,7 @@
MELON: 20
EMERALD_ORE: 40
LAPIS_ORE: 50
+ SUSPICIOUS_GRAVEL: 30
mobs:
WITCH: 100
PARROT: 50
diff --git a/src/main/resources/phases/7000_desert.yml b/src/main/resources/phases/7000_desert.yml
index 9bf8144..a94765e 100644
--- a/src/main/resources/phases/7000_desert.yml
+++ b/src/main/resources/phases/7000_desert.yml
@@ -9,6 +9,7 @@
SANDSTONE: 500
RED_SAND: 100
CHEST: 100
+ SUSPICIOUS_SAND: 20
mobs:
ZOMBIE: 30
SKELETON: 50
diff --git a/src/main/resources/phases/8500_plenty.yml b/src/main/resources/phases/8500_plenty.yml
index 0836aed..758153f 100644
--- a/src/main/resources/phases/8500_plenty.yml
+++ b/src/main/resources/phases/8500_plenty.yml
@@ -44,6 +44,7 @@
EMERALD_ORE: 1
DIRT_PATH: 1
LIGHT_GRAY_TERRACOTTA: 1
+ SUSPICIOUS_SAND: 1
mobs:
COW: 1
DONKEY: 1
diff --git a/src/test/java/world/bentobox/aoneblock/listeners/BlockListenerTest.java b/src/test/java/world/bentobox/aoneblock/listeners/BlockListenerTest.java
index 55e7195..a43cca7 100644
--- a/src/test/java/world/bentobox/aoneblock/listeners/BlockListenerTest.java
+++ b/src/test/java/world/bentobox/aoneblock/listeners/BlockListenerTest.java
@@ -1,15 +1,10 @@
package world.bentobox.aoneblock.listeners;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.beans.IntrospectionException;
@@ -18,9 +13,7 @@
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -61,7 +54,6 @@
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.IslandsManager;
-import world.bentobox.bentobox.managers.PlaceholdersManager;
import world.bentobox.bentobox.managers.PlayersManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level;
@@ -103,8 +95,6 @@ public class BlockListenerTest {
@Mock
private Level level;
@Mock
- private PlaceholdersManager phm;
- @Mock
private Location location;
@Mock
private IslandsManager im;
@@ -140,9 +130,6 @@ public void setUp() throws Exception {
DatabaseType value = DatabaseType.JSON;
when(plugin.getSettings()).thenReturn(pluginSettings);
when(pluginSettings.getDatabaseType()).thenReturn(value);
- when(plugin.getPlaceholdersManager()).thenReturn(phm);
- // Placeholders
- when(phm.replacePlaceholders(any(), anyString())).thenAnswer(a -> (String)a.getArgument(1, String.class));
// Addon
when(addon.getPlugin()).thenReturn(plugin);
@@ -205,34 +192,6 @@ private static void deleteAll(File file) throws IOException {
}
}
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#replacePlaceholders(org.bukkit.entity.Player, java.lang.String, java.lang.String, world.bentobox.bentobox.database.objects.Island, java.util.List)}.
- */
- @Test
- public void testReplacePlaceholders() {
- // Commands
- /*
- * [island] - Island name
- * [owner] - Island owner's name
- * [player] - The name of the player who broke the block triggering the commands
- * [phase] - the name of this phase
- * [blocks] - the number of blocks broken
- * [level] - island level (Requires Levels Addon)
- * [bank-balance] - island bank balance (Requires Bank Addon)
- * [eco-balance] - player's economy balance (Requires Vault and an economy plugin)
-
- */
- List commandList = new ArrayList<>();
-
- commandList.add("no replacement");
- commandList.add("[island] [owner] [phase] [blocks] [level] [bank-balance] [eco-balance]");
- List r = bl.replacePlaceholders(player, "phaseName", "1000", island, commandList);
- assertEquals(2, r.size());
- assertEquals("no replacement", r.get(0));
- assertEquals("island_name tastybento2 phaseName 1000 1000 100000.0 0.0", r.get(1));
- verify(phm, times(2)).replacePlaceholders(eq(player), any());
- }
-
/**
* Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#onBlockFromTo(org.bukkit.event.block.BlockFromToEvent)}
*/
@@ -281,143 +240,5 @@ public void testOnBlockFromToCenterBlock() {
bl.onBlockFromTo(e);
assertTrue(e.isCancelled());
}
-
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
- */
- @Test
- public void testCheckPhase() {
- // Set up that a phase has been completed
- is = new OneBlockIslands(UUID.randomUUID().toString());
- is.setPhaseName("Previous");
- is.setBlockNumber(500);
- is.setLifetime(500L);
- // The phase the user has just moved to
- phase = new OneBlockPhase("500");
- phase.setPhaseName("Next Phase");
- phase.setStartCommands(List.of("start1", "start2"));
-
- // The previous phase
- OneBlockPhase previous = mock(OneBlockPhase.class);
- when(previous.getPhaseName()).thenReturn("Previous");
-
- when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
- assertTrue(bl.checkPhase(player, island, is, phase));
- // Verify commands run
- verify(previous).getEndCommands();
- verify(previous).getFirstTimeEndCommands();
- // Verify title shown
- verify(player).sendTitle("Next Phase", null, -1, -1, -1);
-
- }
-
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
- */
- @Test
- public void testCheckPhaseSecondTime() {
- // Set up that a phase has been completed
- is = new OneBlockIslands(UUID.randomUUID().toString());
- is.setPhaseName("Previous");
- is.setBlockNumber(500);
- is.setLifetime(10500L);
- // The phase the user has just moved to
- phase = new OneBlockPhase("500");
- phase.setPhaseName("Next Phase");
- phase.setStartCommands(List.of("start1", "start2"));
-
- // The previous phase
- OneBlockPhase previous = mock(OneBlockPhase.class);
- when(previous.getPhaseName()).thenReturn("Previous");
-
- when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
- assertTrue(bl.checkPhase(player, island, is, phase));
- // Verify commands run
- verify(previous).getEndCommands();
- verify(previous, never()).getFirstTimeEndCommands();
- // Verify title shown
- verify(player).sendTitle("Next Phase", null, -1, -1, -1);
-
- }
-
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
- */
- @Test
- public void testCheckPhaseNullPlayer() {
- // Set up that a phase has been completed
- is = new OneBlockIslands(UUID.randomUUID().toString());
- is.setPhaseName("Previous");
- is.setBlockNumber(500);
- is.setLifetime(500L);
- // The phase the user has just moved to
- phase = new OneBlockPhase("500");
- phase.setPhaseName("Next Phase");
- phase.setStartCommands(List.of("start1", "start2"));
-
- // The previous phase
- OneBlockPhase previous = mock(OneBlockPhase.class);
- when(previous.getPhaseName()).thenReturn("Previous");
-
- when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
- assertTrue(bl.checkPhase(null, island, is, phase));
- // Verify commands run
- verify(previous).getEndCommands();
- verify(previous).getFirstTimeEndCommands();
- // Verify title shown
- verify(player).sendTitle("Next Phase", null, -1, -1, -1);
-
- }
-
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
- */
- @Test
- public void testCheckPhaseNPCPlayer() {
- when(player.hasMetadata("NPC")).thenReturn(true);
- // Set up that a phase has been completed
- is = new OneBlockIslands(UUID.randomUUID().toString());
- is.setPhaseName("Previous");
- is.setBlockNumber(500);
- is.setLifetime(500L);
- // The phase the user has just moved to
- phase = new OneBlockPhase("500");
- phase.setPhaseName("Next Phase");
- phase.setStartCommands(List.of("start1", "start2"));
-
- // The previous phase
- OneBlockPhase previous = mock(OneBlockPhase.class);
- when(previous.getPhaseName()).thenReturn("Previous");
-
- when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
-
- assertTrue(bl.checkPhase(player, island, is, phase));
- // Verify commands run
- verify(previous).getEndCommands();
- verify(previous).getFirstTimeEndCommands();
- // Verify title shown
- verify(player).sendTitle("Next Phase", null, -1, -1, -1);
-
- }
-
- /**
- * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
- */
- @Test
- public void testCheckSamePhase() {
- is = new OneBlockIslands(UUID.randomUUID().toString());
- is.setPhaseName("Previous");
- is.setBlockNumber(500);
- is.setLifetime(500L);
- // The phase the user has just moved to
- phase = new OneBlockPhase("500");
- phase.setPhaseName("Previous");
-
- assertFalse(bl.checkPhase(player, island, is, phase));
-
- }
-
+
}
diff --git a/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java b/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java
new file mode 100644
index 0000000..6ef0b60
--- /dev/null
+++ b/src/test/java/world/bentobox/aoneblock/listeners/CheckPhaseTest.java
@@ -0,0 +1,320 @@
+package world.bentobox.aoneblock.listeners;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.entity.Player;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.reflect.Whitebox;
+
+import world.bentobox.aoneblock.AOneBlock;
+import world.bentobox.aoneblock.dataobjects.OneBlockIslands;
+import world.bentobox.aoneblock.oneblocks.OneBlockPhase;
+import world.bentobox.aoneblock.oneblocks.OneBlocksManager;
+import world.bentobox.bank.Bank;
+import world.bentobox.bank.BankManager;
+import world.bentobox.bank.data.Money;
+import world.bentobox.bentobox.BentoBox;
+import world.bentobox.bentobox.api.user.User;
+import world.bentobox.bentobox.database.DatabaseSetup;
+import world.bentobox.bentobox.database.objects.Island;
+import world.bentobox.bentobox.managers.IslandsManager;
+import world.bentobox.bentobox.managers.PlaceholdersManager;
+import world.bentobox.bentobox.managers.PlayersManager;
+import world.bentobox.bentobox.util.Util;
+import world.bentobox.level.Level;
+
+/**
+ * @author tastybento
+ *
+ */
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, Util.class})
+public class CheckPhaseTest {
+
+ @Mock
+ BentoBox plugin;
+ @Mock
+ AOneBlock addon;
+ @Mock
+ private World world;
+ @Mock
+ private PlayersManager pm;
+ @Mock
+ private IslandsManager im;
+
+ private @NonNull OneBlockIslands is;
+
+ private @NonNull OneBlockPhase phase;
+ @Mock
+ private OneBlocksManager obm;
+ @Mock
+ private @Nullable Player player;
+ private Island island;
+ private CheckPhase bl;
+ private User user;
+ @Mock
+ private PlaceholdersManager phm;
+ @Mock
+ private BlockListener blis;
+ @Mock
+ private Bank bank;
+ @Mock
+ private Level level;
+
+
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ // Bukkit
+ PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
+ // Set up plugin
+ Whitebox.setInternalState(BentoBox.class, "instance", plugin);
+
+ // Addon
+ when(addon.getPlugin()).thenReturn(plugin);
+ when(addon.getOneBlockManager()).thenReturn(obm);
+ when(addon.getPlayers()).thenReturn(pm);
+ when(addon.getOverWorld()).thenReturn(world);
+ when(addon.getIslands()).thenReturn(im);
+ when(addon.inWorld(world)).thenReturn(true);
+ when(addon.getBlockListener()).thenReturn(blis);
+
+ // Player
+ when(player.getUniqueId()).thenReturn(UUID.randomUUID());
+ when(player.getName()).thenReturn("tastybento");
+ when(player.isOnline()).thenReturn(true);
+ when(player.getWorld()).thenReturn(world);
+ User.setPlugin(plugin);
+ user = User.getInstance(player);
+
+ // Island
+ island = new Island();
+ island.setOwner(UUID.randomUUID());
+ island.setName("island_name");
+ // Players Manager
+ when(pm.getName(any())).thenReturn("tastybento2");
+ when(pm.getUser(any(UUID.class))).thenReturn(user);
+
+ // Bank
+ BankManager bm = mock(BankManager.class);
+ when(bank.getBankManager()).thenReturn(bm);
+ // Phat balance to start
+ when(bm.getBalance(island)).thenReturn(new Money(100000D));
+ when(addon.getAddonByName("Bank")).thenReturn(Optional.of(bank));
+ // Level
+ when(level.getIslandLevel(eq(world), any())).thenReturn(1000L);
+ when(addon.getAddonByName("Level")).thenReturn(Optional.of(level));
+
+
+ // Placeholders
+ when(plugin.getPlaceholdersManager()).thenReturn(phm);
+ when(phm.replacePlaceholders(any(), anyString())).thenAnswer(a -> (String)a.getArgument(1, String.class));
+
+
+ bl = new CheckPhase(addon, blis);
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @After
+ public void tearDown() throws Exception {
+ }
+
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+ */
+ @Test
+ public void testCheckPhase() {
+ // Set up that a phase has been completed
+ is = new OneBlockIslands(UUID.randomUUID().toString());
+ is.setPhaseName("Previous");
+ is.setBlockNumber(500);
+ is.setLifetime(500L);
+ // The phase the user has just moved to
+ phase = new OneBlockPhase("500");
+ phase.setPhaseName("Next Phase");
+ phase.setStartCommands(List.of("start1", "start2"));
+
+ // The previous phase
+ OneBlockPhase previous = mock(OneBlockPhase.class);
+ when(previous.getPhaseName()).thenReturn("Previous");
+
+ when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+ assertTrue(bl.checkPhase(player, island, is, phase));
+ // Verify commands run
+ verify(previous).getEndCommands();
+ verify(previous).getFirstTimeEndCommands();
+ // Verify title shown
+ verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+
+ }
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+ */
+ @Test
+ public void testCheckPhaseSecondTime() {
+ // Set up that a phase has been completed
+ is = new OneBlockIslands(UUID.randomUUID().toString());
+ is.setPhaseName("Previous");
+ is.setBlockNumber(500);
+ is.setLifetime(10500L);
+ // The phase the user has just moved to
+ phase = new OneBlockPhase("500");
+ phase.setPhaseName("Next Phase");
+ phase.setStartCommands(List.of("start1", "start2"));
+
+ // The previous phase
+ OneBlockPhase previous = mock(OneBlockPhase.class);
+ when(previous.getPhaseName()).thenReturn("Previous");
+
+ when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+ assertTrue(bl.checkPhase(player, island, is, phase));
+ // Verify commands run
+ verify(previous).getEndCommands();
+ verify(previous, never()).getFirstTimeEndCommands();
+ // Verify title shown
+ verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+
+ }
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+ */
+ @Test
+ public void testCheckPhaseNullPlayer() {
+ // Set up that a phase has been completed
+ is = new OneBlockIslands(UUID.randomUUID().toString());
+ is.setPhaseName("Previous");
+ is.setBlockNumber(500);
+ is.setLifetime(500L);
+ // The phase the user has just moved to
+ phase = new OneBlockPhase("500");
+ phase.setPhaseName("Next Phase");
+ phase.setStartCommands(List.of("start1", "start2"));
+
+ // The previous phase
+ OneBlockPhase previous = mock(OneBlockPhase.class);
+ when(previous.getPhaseName()).thenReturn("Previous");
+
+ when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+ assertTrue(bl.checkPhase(null, island, is, phase));
+ // Verify commands run
+ verify(previous).getEndCommands();
+ verify(previous).getFirstTimeEndCommands();
+ // Verify title shown
+ verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+
+ }
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+ */
+ @Test
+ public void testCheckPhaseNPCPlayer() {
+ when(player.hasMetadata("NPC")).thenReturn(true);
+ // Set up that a phase has been completed
+ is = new OneBlockIslands(UUID.randomUUID().toString());
+ is.setPhaseName("Previous");
+ is.setBlockNumber(500);
+ is.setLifetime(500L);
+ // The phase the user has just moved to
+ phase = new OneBlockPhase("500");
+ phase.setPhaseName("Next Phase");
+ phase.setStartCommands(List.of("start1", "start2"));
+
+ // The previous phase
+ OneBlockPhase previous = mock(OneBlockPhase.class);
+ when(previous.getPhaseName()).thenReturn("Previous");
+
+ when(obm.getPhase("Previous")).thenReturn(Optional.of(previous));
+
+ assertTrue(bl.checkPhase(player, island, is, phase));
+ // Verify commands run
+ verify(previous).getEndCommands();
+ verify(previous).getFirstTimeEndCommands();
+ // Verify title shown
+ verify(player).sendTitle("Next Phase", null, -1, -1, -1);
+
+ }
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.CheckPhase#checkPhase(Player, Island, world.bentobox.aoneblock.dataobjects.OneBlockIslands, world.bentobox.aoneblock.oneblocks.OneBlockPhase)}
+ */
+ @Test
+ public void testCheckSamePhase() {
+ is = new OneBlockIslands(UUID.randomUUID().toString());
+ is.setPhaseName("Previous");
+ is.setBlockNumber(500);
+ is.setLifetime(500L);
+ // The phase the user has just moved to
+ phase = new OneBlockPhase("500");
+ phase.setPhaseName("Previous");
+
+ assertFalse(bl.checkPhase(player, island, is, phase));
+
+ }
+
+ /**
+ * Test method for {@link world.bentobox.aoneblock.listeners.BlockListener#replacePlaceholders(org.bukkit.entity.Player, java.lang.String, java.lang.String, world.bentobox.bentobox.database.objects.Island, java.util.List)}.
+ */
+ @Test
+ public void testReplacePlaceholders() {
+ // Commands
+ /*
+ * [island] - Island name
+ * [owner] - Island owner's name
+ * [player] - The name of the player who broke the block triggering the commands
+ * [phase] - the name of this phase
+ * [blocks] - the number of blocks broken
+ * [level] - island level (Requires Levels Addon)
+ * [bank-balance] - island bank balance (Requires Bank Addon)
+ * [eco-balance] - player's economy balance (Requires Vault and an economy plugin)
+
+ */
+ List commandList = new ArrayList<>();
+
+ commandList.add("no replacement");
+ commandList.add("[island] [owner] [phase] [blocks] [level] [bank-balance] [eco-balance]");
+ List r = bl.replacePlaceholders(player, "phaseName", "1000", island, commandList);
+ assertEquals(2, r.size());
+ assertEquals("no replacement", r.get(0));
+ assertEquals("island_name tastybento2 phaseName 1000 1000 100000.0 0.0", r.get(1));
+ verify(phm, times(2)).replacePlaceholders(eq(player), any());
+ }
+}