From 29fd573d0ceee8e4a37fc87fa3a42153e2a75375 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 17 Oct 2019 22:37:27 -0700 Subject: [PATCH] Prevents mobs from spawning in the greenhouse wall https://github.com/BentoBoxWorld/Greenhouses/issues/26 Some large mobs can spawn next to the wall and pop out of the greenhouse. This deletes them if they are too big. --- pom.xml | 2 +- .../greenhouses/greenhouse/BiomeRecipe.java | 28 +++++++++- .../greenhouses/managers/GreenhouseMap.java | 12 ++++ .../greenhouse/BiomeRecipeTest.java | 55 +++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index e8e0c23..01fb013 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ ${build.version}-SNAPSHOT - 0.4.1 + 0.4.2 -LOCAL diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java index d3ab6f9..ce44e69 100644 --- a/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java @@ -12,6 +12,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.stream.Collectors; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.block.Biome; @@ -19,7 +20,10 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.Bisected; import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.Vector; import world.bentobox.bentobox.util.Util; import world.bentobox.greenhouses.Greenhouses; @@ -294,11 +298,31 @@ public class BiomeRecipe implements Comparable { if (b.getY() == 0) { return false; } + // Center spawned mob + Location spawnLoc = b.getLocation().clone().add(new Vector(0.5, 0, 0.5)); return getRandomMob() // Check if the spawn on block matches, if it exists .filter(m -> m.getMobSpawnOn().map(b.getRelative(BlockFace.DOWN).getType()::equals).orElse(true)) - // If spawn occurs, return true - .map(m -> b.getWorld().spawnEntity(b.getLocation(), m.getMobType()) != null).orElse(false); + // If spawn occurs, check if it can fit inside greenhouse + .map(m -> { + Entity entity = b.getWorld().spawnEntity(spawnLoc, m.getMobType()); + if (entity != null) { + return addon + .getManager() + .getMap() + .getGreenhouse(b.getLocation()).map(gh -> { + BoundingBox interior = gh.getBoundingBox().clone(); + interior.expand(-1, -1, -1); + if (!interior.contains(entity.getBoundingBox())) { + entity.remove(); + return false; + } + return true; + }).orElse(false); + } + return false; + }).orElse(false); + } /** diff --git a/src/main/java/world/bentobox/greenhouses/managers/GreenhouseMap.java b/src/main/java/world/bentobox/greenhouses/managers/GreenhouseMap.java index 223e7a2..2c3bbc2 100644 --- a/src/main/java/world/bentobox/greenhouses/managers/GreenhouseMap.java +++ b/src/main/java/world/bentobox/greenhouses/managers/GreenhouseMap.java @@ -78,6 +78,16 @@ public class GreenhouseMap { return getGreenhouse(location).isPresent(); } + /** + * Check if a location is in a specific greenhouse + * @param gh - greenhouse + * @param location - location to check + * @return true if inside + */ + public boolean inGreenhouse(Greenhouse gh, Location location) { + return getGreenhouse(location).map(gh::equals).orElse(false); + } + /** * Check if location is above a greenhouse * @param location - location @@ -135,4 +145,6 @@ public class GreenhouseMap { public int getSize() { return greenhouses.values().stream().mapToInt(List::size).sum(); } + + } diff --git a/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java b/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java index 0b4d3ea..645cf20 100644 --- a/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java +++ b/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java @@ -12,6 +12,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.util.Optional; import java.util.Set; import org.bukkit.Bukkit; @@ -28,7 +29,9 @@ import org.bukkit.block.data.BlockData; import org.bukkit.entity.Cat; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; +import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.util.BoundingBox; +import org.bukkit.util.Vector; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,7 +43,9 @@ import org.powermock.modules.junit4.PowerMockRunner; import world.bentobox.bentobox.BentoBox; import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.data.Greenhouse; +import world.bentobox.greenhouses.managers.GreenhouseManager; import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult; +import world.bentobox.greenhouses.managers.GreenhouseMap; /** * @author tastybento @@ -67,6 +72,14 @@ public class BiomeRecipeTest { private Location location; @Mock private BlockData bd; + @Mock + private BentoBox plugin; + @Mock + private GreenhouseManager mgr; + @Mock + private GreenhouseMap map; + @Mock + private BukkitScheduler scheduler; /** * @throws java.lang.Exception @@ -93,6 +106,8 @@ public class BiomeRecipeTest { Material.AIR); when(block.getWorld()).thenReturn(world); when(block.getLocation()).thenReturn(location); + when(location.clone()).thenReturn(location); + when(location.add(any(Vector.class))).thenReturn(location); // Set up default recipe br = new BiomeRecipe(addon, type, 0); br.setIcecoverage(2); // 1% @@ -103,6 +118,17 @@ public class BiomeRecipeTest { br.setName("name2"); br.setIcon(Material.ACACIA_BOAT); br.setPermission("perm"); + // Plugin + when(addon.getPlugin()).thenReturn(plugin); + // Manager + when(addon.getManager()).thenReturn(mgr); + // GH Map + when(mgr.getMap()).thenReturn(map); + Optional optionalGh = Optional.of(gh); + when(map.getGreenhouse(any(Location.class))).thenReturn(optionalGh); + // Bukkit Scheduler + when(Bukkit.getScheduler()).thenReturn(scheduler); + } /** @@ -360,6 +386,31 @@ public class BiomeRecipeTest { assertFalse(br.spawnMob(block)); } + /** + * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#spawnMob(org.bukkit.block.Block)}. + */ + @Test + public void testSpawnMobOutsideWall() { + when(block.getY()).thenReturn(10); + when(block.getType()).thenReturn(Material.GRASS_PATH); + when(block.getRelative(any())).thenReturn(block); + + EntityType mobType = EntityType.CAT; + int mobProbability = 100; + Material mobSpawnOn = Material.GRASS_PATH; + + Entity cat = mock(Cat.class); + // Same box as greenhouse + when(cat.getBoundingBox()).thenReturn(bb); + when(world.spawnEntity(any(), any())).thenReturn(cat); + + + br.addMobs(mobType, mobProbability, mobSpawnOn); + assertFalse(br.spawnMob(block)); + verify(world).spawnEntity(eq(location), eq(EntityType.CAT)); + verify(location).add(any(Vector.class)); + } + /** * Test method for {@link world.bentobox.greenhouses.greenhouse.BiomeRecipe#spawnMob(org.bukkit.block.Block)}. */ @@ -374,12 +425,16 @@ public class BiomeRecipeTest { Material mobSpawnOn = Material.GRASS_PATH; Entity cat = mock(Cat.class); + // Exactly 1 block smaller than the greenhouse blocks + BoundingBox small = new BoundingBox(11, 101, 11, 19, 119, 19); + when(cat.getBoundingBox()).thenReturn(small); when(world.spawnEntity(any(), any())).thenReturn(cat); br.addMobs(mobType, mobProbability, mobSpawnOn); assertTrue(br.spawnMob(block)); verify(world).spawnEntity(eq(location), eq(EntityType.CAT)); + verify(location).add(any(Vector.class)); } /**