diff --git a/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java index 07c6571..b44ae5d 100644 --- a/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java +++ b/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java @@ -2,6 +2,7 @@ package world.bentobox.greenhouses.greenhouse; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.EnumMap; import java.util.HashSet; import java.util.List; @@ -24,6 +25,7 @@ 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.block.data.Waterlogged; import org.bukkit.block.data.type.GlowLichen; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; @@ -35,6 +37,7 @@ import com.google.common.base.Enums; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.util.Util; import world.bentobox.greenhouses.Greenhouses; import world.bentobox.greenhouses.data.Greenhouse; @@ -61,12 +64,26 @@ public class BiomeRecipe implements Comparable { } private static final List ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST); + private static final List UNDERWATER_PLANTS; + static { + List m = new ArrayList<>(); + Arrays.stream(Material.values()).filter(c -> c.name().contains("CORAL")).forEach(m::add); + m.add(Material.SEA_LANTERN); + m.add(Material.SEA_PICKLE); + m.add(Material.SEAGRASS); + m.add(Material.KELP); + m.add(Material.GLOW_LICHEN); + UNDERWATER_PLANTS = Collections.unmodifiableList(m); + } // Content requirements // Material, Type, Qty. There can be more than one type of material required private final Map requiredBlocks = new EnumMap<>(Material.class); - // Plants + /** + * Tree map of plants + */ private final TreeMap plantTree = new TreeMap<>(); + private final TreeMap underwaterPlants = new TreeMap<>(); // Mobs // Entity Type, Material to Spawn on, Probability @@ -149,11 +166,12 @@ public class BiomeRecipe implements Comparable { */ public boolean addPlants(Material plantMaterial, double plantProbability, Material plantGrowOn) { double probability = plantProbability/100; + TreeMap map = UNDERWATER_PLANTS.contains(plantMaterial) ? underwaterPlants : plantTree; // Add up all the probabilities in the list so far - double lastProb = plantTree.isEmpty() ? 0D : plantTree.lastKey(); + double lastProb = map.isEmpty() ? 0D : map.lastKey(); if ((1D - lastProb) >= probability) { // Add to probability tree - plantTree.put(lastProb + probability, new GreenhousePlant(plantMaterial, plantGrowOn)); + map.put(lastProb + probability, new GreenhousePlant(plantMaterial, plantGrowOn)); } else { addon.logError("Plant chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + plantMaterial.toString()); return false; @@ -358,7 +376,9 @@ public class BiomeRecipe implements Comparable { 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 -> Optional.of(m.mobSpawnOn()).map(b.getRelative(BlockFace.DOWN).getType()::equals).orElse(true)) + .filter(m -> Optional.of(m.mobSpawnOn()) + .map(b.getRelative(BlockFace.DOWN).getType()::equals) + .orElse(true)) // If spawn occurs, check if it can fit inside greenhouse .map(m -> { Entity entity = b.getWorld().spawnEntity(spawnLoc, m.mobType()); @@ -409,11 +429,11 @@ public class BiomeRecipe implements Comparable { return key == null ? Optional.empty() : Optional.ofNullable(mobTree.get(key)); } - private Optional getRandomPlant() { + private Optional getRandomPlant(boolean underwater) { // Grow a random plant that can grow double r = random.nextDouble(); - Double key = plantTree.ceilingKey(r); - return key == null ? Optional.empty() : Optional.ofNullable(plantTree.get(key)); + Double key = underwater ? underwaterPlants.ceilingKey(r) : plantTree.ceilingKey(r); + return key == null ? Optional.empty() : Optional.ofNullable(underwater ? underwaterPlants.get(key) : plantTree.get(key)); } /** @@ -433,20 +453,17 @@ public class BiomeRecipe implements Comparable { /** * Plants a plant on block bl if it makes sense. * @param block - block that can have growth + * @param underwater - if the block is underwater or not * @return true if successful */ - public boolean growPlant(GrowthBlock block) { + public boolean growPlant(GrowthBlock block, boolean underwater) { Block bl = block.block(); - if (!bl.isEmpty()) { - return false; - } - return getRandomPlant().map(p -> { + return getRandomPlant(underwater).map(p -> { if (bl.getY() != 0 && canGrowOn(block, p)) { - if (!plantIt(bl, p)) { - return false; + if (plantIt(bl, p)) { + bl.getWorld().spawnParticle(Particle.SNOWBALL, bl.getLocation(), 10, 2, 2, 2); + return true; } - bl.getWorld().spawnParticle(Particle.SNOWBALL, bl.getLocation(), 10, 2, 2, 2); - return true; } return false; }).orElse(false); @@ -459,6 +476,7 @@ public class BiomeRecipe implements Comparable { * @return true if successful, false if not */ private boolean plantIt(Block bl, GreenhousePlant p) { + boolean underwater = bl.getType().equals(Material.WATER); BlockData dataBottom = p.plantMaterial().createBlockData(); // Check if this is a double-height plant if (dataBottom instanceof Bisected bi) { @@ -475,8 +493,13 @@ public class BiomeRecipe implements Comparable { } else if (p.plantMaterial().equals(Material.GLOW_LICHEN)) { return placeLichen(bl); } else { - // Single height plant - bl.setBlockData(dataBottom, false); + if (dataBottom instanceof Waterlogged wl) { + wl.setWaterlogged(underwater); + bl.setBlockData(wl, false); + } else { + // Single height plant + bl.setBlockData(dataBottom, false); + } } return true; } @@ -528,12 +551,31 @@ public class BiomeRecipe implements Comparable { return false; } + /** + * Checks if a particular plant can group at the location of a block + * @param block - block being checked + * @param p - greenhouse plant + * @return true if it can be grown otherwise false + */ private boolean canGrowOn(GrowthBlock block, GreenhousePlant p) { // Ceiling plants can only grow on ceiling blocks if (CEILING_PLANTS.contains(p.plantMaterial()) && Boolean.TRUE.equals(block.floor())) { return false; } - return p.plantGrownOn().equals(block.block().getRelative(Boolean.TRUE.equals(block.floor()) ? BlockFace.DOWN : BlockFace.UP).getType()); + // Underwater plants can only be placed in water and regular plants cannot be placed in water + if (block.block().getType().equals(Material.WATER)) { + BentoBox.getInstance().logDebug("Water block"); + if (!UNDERWATER_PLANTS.contains(p.plantMaterial())) { + return false; + } + BentoBox.getInstance().logDebug("Water plant"); + } else if (UNDERWATER_PLANTS.contains(p.plantMaterial())) { + return false; + } + + return p.plantGrownOn().equals(block.block().getRelative(Boolean.TRUE.equals(block.floor()) ? + BlockFace.DOWN : + BlockFace.UP).getType()); } /** diff --git a/src/main/java/world/bentobox/greenhouses/managers/EcoSystemManager.java b/src/main/java/world/bentobox/greenhouses/managers/EcoSystemManager.java index d7ee49a..f8bd434 100644 --- a/src/main/java/world/bentobox/greenhouses/managers/EcoSystemManager.java +++ b/src/main/java/world/bentobox/greenhouses/managers/EcoSystemManager.java @@ -183,9 +183,13 @@ public class EcoSystemManager { int bonemeal = getBoneMeal(gh); if (bonemeal > 0) { // Get a list of all available blocks - List list = getAvailableBlocks(gh, true); + List list = getAvailableBlocks(gh, false); Collections.shuffle(list); - int plantsGrown = list.stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl) ? 1 : 0).sum(); + int plantsGrown = list.stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl, false) ? 1 : 0).sum(); + // Underwater plants + list = getAvailableBlocks(gh, true); + Collections.shuffle(list); + plantsGrown += list.stream().limit(bonemeal).mapToInt(bl -> gh.getBiomeRecipe().growPlant(bl, true) ? 1 : 0).sum(); if (plantsGrown > 0) { setBoneMeal(gh, bonemeal - (int)Math.ceil((double)plantsGrown / PLANTS_PER_BONEMEAL )); } @@ -208,6 +212,7 @@ public class EcoSystemManager { } + public record GrowthBlock(Block block, Boolean floor) {} /** * Get a list of the lowest level blocks inside the greenhouse. May be air, liquid or plants. @@ -225,26 +230,31 @@ public class EcoSystemManager { for (double z = ibb.getMinZ(); z < ibb.getMaxZ(); z++) { for (double y = ibb.getMaxY() - 1; y >= bb.getMinY(); y--) { Block b = gh.getWorld().getBlockAt(NumberConversions.floor(x), NumberConversions.floor(y), NumberConversions.floor(z)); - // Check ceiling blocks - if (b.isEmpty() && !b.getRelative(BlockFace.UP).isEmpty()) { - result.add(new GrowthBlock(b, false)); - } // Check floor blocks - if (!(b.isEmpty() || (ignoreLiquid && b.isLiquid())) - && (b.getRelative(BlockFace.UP).isEmpty() - || (b.getRelative(BlockFace.UP).isPassable() && !b.isLiquid()) - || (ignoreLiquid && b.isLiquid() && b.getRelative(BlockFace.UP).isPassable()))) { - result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true)); - break; + if (!ignoreLiquid) { + // Check ceiling blocks + if (b.isEmpty() && !b.getRelative(BlockFace.UP).isEmpty()) { + result.add(new GrowthBlock(b, false)); + } + if (!b.isEmpty() && (b.getRelative(BlockFace.UP).isEmpty() || b.getRelative(BlockFace.UP).isPassable())) { + result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true)); + break; + } + } else { + if (!b.isEmpty() && !b.isLiquid() && b.getRelative(BlockFace.UP).isLiquid()) { + result.add(new GrowthBlock(b.getRelative(BlockFace.UP), true)); + break; + } } + } } } return result; } - public record GrowthBlock(Block block, Boolean floor) {} + private int getBoneMeal(Greenhouse gh) { Hopper hopper = getHopper(gh); diff --git a/src/main/java/world/bentobox/greenhouses/managers/RecipeManager.java b/src/main/java/world/bentobox/greenhouses/managers/RecipeManager.java index 7791648..248ba15 100644 --- a/src/main/java/world/bentobox/greenhouses/managers/RecipeManager.java +++ b/src/main/java/world/bentobox/greenhouses/managers/RecipeManager.java @@ -1,8 +1,14 @@ package world.bentobox.greenhouses.managers; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map.Entry; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import org.bukkit.ChatColor; diff --git a/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java b/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java index 8df65c1..db64690 100644 --- a/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java +++ b/src/test/java/world/bentobox/greenhouses/greenhouse/BiomeRecipeTest.java @@ -611,7 +611,7 @@ public class BiomeRecipeTest { @Test public void testGrowPlantNotAir() { when(block.getType()).thenReturn(Material.SOUL_SAND); - assertFalse(br.growPlant(new GrowthBlock(block, true))); + assertFalse(br.growPlant(new GrowthBlock(block, true), false)); } /** @@ -621,7 +621,7 @@ public class BiomeRecipeTest { public void testGrowPlantNoPlants() { when(block.getType()).thenReturn(Material.AIR); when(block.isEmpty()).thenReturn(true); - assertFalse(br.growPlant(new GrowthBlock(block, true))); + assertFalse(br.growPlant(new GrowthBlock(block, true), false)); } /** @@ -633,7 +633,7 @@ public class BiomeRecipeTest { when(block.getType()).thenReturn(Material.AIR); when(block.isEmpty()).thenReturn(true); assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK)); - assertFalse(br.growPlant(new GrowthBlock(block, true))); + assertFalse(br.growPlant(new GrowthBlock(block, true), false)); } /** @@ -649,7 +649,7 @@ public class BiomeRecipeTest { when(block.getRelative(any())).thenReturn(ob); assertTrue(br.addPlants(Material.BAMBOO_SAPLING, 100, Material.GRASS_BLOCK)); - assertTrue(br.growPlant(new GrowthBlock(block, true))); + assertTrue(br.growPlant(new GrowthBlock(block, true), false)); verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble()); verify(block).setBlockData(eq(bd), eq(false)); } @@ -667,7 +667,7 @@ public class BiomeRecipeTest { when(block.getRelative(any())).thenReturn(ob); assertTrue(br.addPlants(Material.SPORE_BLOSSOM, 100, Material.GLASS)); - assertTrue(br.growPlant(new GrowthBlock(block, false))); + assertTrue(br.growPlant(new GrowthBlock(block, false), false)); verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble()); verify(block).setBlockData(eq(bd), eq(false)); } @@ -686,7 +686,7 @@ public class BiomeRecipeTest { when(block.getRelative(any())).thenReturn(ob); assertTrue(br.addPlants(Material.SPORE_BLOSSOM, 100, Material.GLASS)); // Not a ceiling block - assertFalse(br.growPlant(new GrowthBlock(block, true))); + assertFalse(br.growPlant(new GrowthBlock(block, true), false)); } /** @@ -704,7 +704,7 @@ public class BiomeRecipeTest { when(block.getRelative(BlockFace.DOWN)).thenReturn(ob); when(block.getRelative(BlockFace.UP)).thenReturn(block); assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK)); - assertTrue(br.growPlant(new GrowthBlock(block, true))); + assertTrue(br.growPlant(new GrowthBlock(block, true), false)); verify(world).spawnParticle(eq(Particle.SNOWBALL), any(Location.class), anyInt(), anyDouble(), anyDouble(), anyDouble()); verify(bisected).setHalf(eq(Half.BOTTOM)); verify(bisected).setHalf(eq(Half.TOP)); @@ -725,7 +725,7 @@ public class BiomeRecipeTest { when(block.getRelative(eq(BlockFace.DOWN))).thenReturn(ob); when(block.getRelative(eq(BlockFace.UP))).thenReturn(ob); assertTrue(br.addPlants(Material.SUNFLOWER, 100, Material.GRASS_BLOCK)); - assertFalse(br.growPlant(new GrowthBlock(block, true))); + assertFalse(br.growPlant(new GrowthBlock(block, true), false)); } /** diff --git a/src/test/java/world/bentobox/greenhouses/managers/EcoSystemManagerTest.java b/src/test/java/world/bentobox/greenhouses/managers/EcoSystemManagerTest.java index 9364d72..6111923 100644 --- a/src/test/java/world/bentobox/greenhouses/managers/EcoSystemManagerTest.java +++ b/src/test/java/world/bentobox/greenhouses/managers/EcoSystemManagerTest.java @@ -123,6 +123,17 @@ public class EcoSystemManagerTest { when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid); when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid); List result = eco.getAvailableBlocks(gh, false); + assertEquals(16, result.size()); + } + + /** + * Test method for {@link world.bentobox.greenhouses.managers.EcoSystemManager#getAvailableBlocks(Greenhouse, boolean)}. + */ + @Test + public void testGetAvailableBlocksAllLiquid2() { + when(liquid.getRelative(eq(BlockFace.UP))).thenReturn(liquid); + when(world.getBlockAt(anyInt(), anyInt(), anyInt())).thenReturn(liquid); + List result = eco.getAvailableBlocks(gh, true); assertEquals(0, result.size()); }