Greenhouses/src/main/java/world/bentobox/greenhouses/greenhouse/BiomeRecipe.java

747 lines
26 KiB
Java
Raw Normal View History

2019-01-19 16:52:04 +01:00
package world.bentobox.greenhouses.greenhouse;
import java.util.ArrayList;
2019-01-19 16:52:04 +01:00
import java.util.Arrays;
import java.util.Collections;
2019-11-01 05:36:05 +01:00
import java.util.EnumMap;
2019-01-22 00:44:01 +01:00
import java.util.HashSet;
2019-01-19 16:52:04 +01:00
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
2019-01-22 00:44:01 +01:00
import java.util.Set;
2019-01-19 16:52:04 +01:00
import java.util.TreeMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
2019-01-19 16:52:04 +01:00
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.Location;
2019-01-19 16:52:04 +01:00
import org.bukkit.Material;
2019-07-08 00:45:47 +02:00
import org.bukkit.Particle;
import org.bukkit.World.Environment;
2019-01-19 16:52:04 +01:00
import org.bukkit.block.Biome;
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;
2023-01-02 07:29:02 +01:00
import org.bukkit.block.data.type.Cocoa;
import org.bukkit.block.data.type.GlowLichen;
import org.bukkit.entity.Entity;
2019-01-19 16:52:04 +01:00
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Hoglin;
import org.bukkit.entity.Piglin;
import org.bukkit.util.Vector;
2019-01-19 16:52:04 +01:00
import com.google.common.base.Enums;
2020-08-16 21:49:27 +02:00
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
2019-01-19 16:52:04 +01:00
import world.bentobox.bentobox.util.Util;
import world.bentobox.greenhouses.Greenhouses;
2019-01-22 00:44:01 +01:00
import world.bentobox.greenhouses.data.Greenhouse;
import world.bentobox.greenhouses.managers.EcoSystemManager.GrowthBlock;
2019-01-22 00:44:01 +01:00
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult;
import world.bentobox.greenhouses.world.AsyncWorldCache;
2019-01-19 16:52:04 +01:00
2019-01-22 00:44:01 +01:00
public class BiomeRecipe implements Comparable<BiomeRecipe> {
2019-11-01 05:36:05 +01:00
private static final String CHANCE_FOR = "% chance for ";
private Greenhouses addon;
2019-01-19 16:52:04 +01:00
private Biome type;
private Material icon; // Biome icon for control panel
private int priority;
private String name;
private String friendlyName;
private static final List<Material> CEILING_PLANTS = new ArrayList<>();
static {
CEILING_PLANTS.add(Material.VINE);
Enums.getIfPresent(Material.class, "SPORE_BLOSSOM").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "CAVE_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "TWISTING_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
Enums.getIfPresent(Material.class, "WEEPING_VINES_PLANT").toJavaUtil().ifPresent(CEILING_PLANTS::add);
}
private static final List<BlockFace> ADJ_BLOCKS = Arrays.asList( BlockFace.DOWN, BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.UP, BlockFace.WEST);
2023-01-02 07:29:02 +01:00
private static final List<BlockFace> SIDE_BLOCKS = Arrays.asList( BlockFace.EAST, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST);
private static final List<Material> UNDERWATER_PLANTS;
static {
List<Material> 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);
}
2019-01-19 16:52:04 +01:00
// Content requirements
// Material, Type, Qty. There can be more than one type of material required
2019-11-01 05:36:05 +01:00
private final Map<Material, Integer> requiredBlocks = new EnumMap<>(Material.class);
/**
* Tree map of plants
*/
2019-01-26 17:38:13 +01:00
private final TreeMap<Double, GreenhousePlant> plantTree = new TreeMap<>();
private final TreeMap<Double, GreenhousePlant> underwaterPlants = new TreeMap<>();
2019-01-19 16:52:04 +01:00
// Mobs
// Entity Type, Material to Spawn on, Probability
2019-01-26 17:38:13 +01:00
private final TreeMap<Double, GreenhouseMob> mobTree = new TreeMap<>();
2019-01-19 16:52:04 +01:00
// Conversions
// Original Material, Original Type, New Material, New Type, Probability
2021-08-01 08:10:07 +02:00
private final Multimap<Material, GreenhouseBlockConversions> conversionBlocks = ArrayListMultimap.create();
2019-01-19 16:52:04 +01:00
private int mobLimit;
private int waterCoverage;
private int iceCoverage;
private int lavaCoverage;
private String permission = "";
2019-01-26 17:38:13 +01:00
private final Random random = new Random();
2023-03-01 17:26:34 +01:00
private int maxMob;
2019-01-19 16:52:04 +01:00
2021-08-02 00:26:21 +02:00
/**
* Create a degenerate recipe with nothing in it
*/
public BiomeRecipe() {}
2019-01-19 16:52:04 +01:00
/**
2019-01-26 17:38:13 +01:00
* @param type - biome
* @param priority - priority (higher is better)
2019-01-19 16:52:04 +01:00
*/
2019-01-22 00:44:01 +01:00
public BiomeRecipe(Greenhouses addon, Biome type, int priority) {
2019-01-26 17:38:13 +01:00
this.addon = addon;
2019-01-19 16:52:04 +01:00
this.type = type;
this.priority = priority;
mobLimit = 9; // Default
}
2020-08-16 21:49:27 +02:00
private void startupLog(String message) {
if (addon.getSettings().isStartupLog()) addon.log(message);
}
2019-01-19 16:52:04 +01:00
/**
* @param oldMaterial - material that will convert
* @param newMaterial - what it will convert to
* @param convChance - percentage chance
* @param localMaterial - what material must be next to it for conversion to happen
*/
public void addConvBlocks(Material oldMaterial, Material newMaterial, double convChance, Material localMaterial) {
double probability = Math.min(convChance/100 , 1D);
conversionBlocks.put(oldMaterial, new GreenhouseBlockConversions(oldMaterial, newMaterial, probability, localMaterial));
startupLog(" " + convChance + CHANCE_FOR + Util.prettifyText(oldMaterial.toString()) + " to convert to " + Util.prettifyText(newMaterial.toString()));
2019-01-19 16:52:04 +01:00
}
/**
2019-01-26 17:38:13 +01:00
* @param mobType - entity type
* @param mobProbability - relative probability
2019-01-26 17:38:13 +01:00
* @param mobSpawnOn - material to spawn on
2019-10-13 02:09:38 +02:00
* @return true if add is successful
2019-01-19 16:52:04 +01:00
*/
public boolean addMobs(EntityType mobType, double mobProbability, Material mobSpawnOn) {
startupLog(" " + mobProbability + CHANCE_FOR + Util.prettifyText(mobType.toString()) + " to spawn on " + Util.prettifyText(mobSpawnOn.toString())+ ".");
double probability = mobProbability/100;
2019-01-22 00:44:01 +01:00
double lastProb = mobTree.isEmpty() ? 0D : mobTree.lastKey();
2019-01-19 16:52:04 +01:00
// Add up all the probabilities in the list so far
2019-01-22 00:44:01 +01:00
if ((1D - lastProb) >= probability) {
2019-01-19 16:52:04 +01:00
// Add to probability tree
2019-01-22 00:44:01 +01:00
mobTree.put(lastProb + probability, new GreenhouseMob(mobType, mobSpawnOn));
2019-10-13 02:09:38 +02:00
return true;
2019-01-19 16:52:04 +01:00
} else {
2021-08-01 08:10:07 +02:00
addon.logError("Mob chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + mobType);
2019-10-13 02:09:38 +02:00
return false;
2019-01-19 16:52:04 +01:00
}
}
/**
* Creates a list of plants that can grow, the probability and what they must grow on.
* Data is drawn from the file biomes.yml
* @param plantMaterial - plant type
* @param plantProbability - probability of growing
* @param plantGrowOn - material on which it must grow
2019-10-13 02:09:38 +02:00
* @return true if add is successful
2019-01-19 16:52:04 +01:00
*/
public boolean addPlants(Material plantMaterial, double plantProbability, Material plantGrowOn) {
double probability = plantProbability/100;
TreeMap<Double, GreenhousePlant> map = UNDERWATER_PLANTS.contains(plantMaterial) ? underwaterPlants : plantTree;
2019-01-19 16:52:04 +01:00
// Add up all the probabilities in the list so far
double lastProb = map.isEmpty() ? 0D : map.lastKey();
2019-01-22 00:44:01 +01:00
if ((1D - lastProb) >= probability) {
2019-01-19 16:52:04 +01:00
// Add to probability tree
map.put(lastProb + probability, new GreenhousePlant(plantMaterial, plantGrowOn));
2019-01-19 16:52:04 +01:00
} else {
2019-01-26 17:38:13 +01:00
addon.logError("Plant chances add up to > 100% in " + type.toString() + " biome recipe! Skipping " + plantMaterial.toString());
2019-10-13 02:09:38 +02:00
return false;
2019-01-19 16:52:04 +01:00
}
startupLog(" " + plantProbability + CHANCE_FOR + Util.prettifyText(plantMaterial.toString()) + " to grow on " + Util.prettifyText(plantGrowOn.toString()));
2019-10-13 02:09:38 +02:00
return true;
2019-01-19 16:52:04 +01:00
}
/**
2019-01-26 17:38:13 +01:00
* @param blockMaterial - block material
* @param blockQty - number of blocks required
2019-01-19 16:52:04 +01:00
*/
public void addReqBlocks(Material blockMaterial, int blockQty) {
requiredBlocks.put(blockMaterial, blockQty);
startupLog(" " + blockMaterial + " x " + blockQty);
2019-01-19 16:52:04 +01:00
}
/**
2019-01-25 04:11:59 +01:00
* Checks greenhouse meets recipe requirements.
* @return GreenhouseResult - result
2019-01-22 00:44:01 +01:00
*/
public CompletableFuture<Set<GreenhouseResult>> checkRecipe(Greenhouse gh) {
CompletableFuture<Set<GreenhouseResult>> r = new CompletableFuture<>();
Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> checkRecipeAsync(r, gh));
return r;
}
/**
* Check greenhouse meets recipe requirements. Expected to be run async.
* @param r - future to complete when done
* @param gh - greenhouse
* @return set of results from the check
*/
private Set<GreenhouseResult> checkRecipeAsync(CompletableFuture<Set<GreenhouseResult>> r, Greenhouse gh) {
2023-06-04 17:44:55 +02:00
AsyncWorldCache cache = new AsyncWorldCache(addon, gh.getWorld());
2019-01-22 00:44:01 +01:00
long area = gh.getArea();
2019-01-19 16:52:04 +01:00
// Look through the greenhouse and count what is in there
2023-06-04 17:55:02 +02:00
Map<Material, Integer> blockCount = countBlocks(gh, cache);
// Calculate % water, ice and lava ratios and check them
Set<GreenhouseResult> result = checkRatios(blockCount, area);
// Compare to the required blocks
Map<Material, Integer> missingBlocks = requiredBlocks.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue() - blockCount.getOrDefault(e.getKey(), 0)));
// Remove any entries that are 0 or less
missingBlocks.values().removeIf(v -> v <= 0);
if (!missingBlocks.isEmpty()) {
result.add(GreenhouseResult.FAIL_INSUFFICIENT_BLOCKS);
gh.setMissingBlocks(missingBlocks);
2019-01-19 16:52:04 +01:00
}
2023-06-04 17:55:02 +02:00
// Return to main thread to complete
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> r.complete(result));
return result;
}
private Set<GreenhouseResult> checkRatios(Map<Material, Integer> blockCount, long area) {
Set<GreenhouseResult> result = new HashSet<>();
2019-01-19 16:52:04 +01:00
double waterRatio = (double)blockCount.getOrDefault(Material.WATER, 0)/area * 100;
double lavaRatio = (double)blockCount.getOrDefault(Material.LAVA, 0)/area * 100;
int ice = blockCount.entrySet().stream().filter(en -> en.getKey().equals(Material.ICE)
|| en.getKey().equals(Material.BLUE_ICE)
|| en.getKey().equals(Material.PACKED_ICE))
.mapToInt(Map.Entry::getValue).sum();
double iceRatio = (double)ice/(double)area * 100;
// Check required ratios - a zero means none of these are allowed, e.g.desert has no water
if (waterCoverage == 0 && waterRatio > 0) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_NO_WATER);
2019-01-19 16:52:04 +01:00
}
if (lavaCoverage == 0 && lavaRatio > 0) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_NO_LAVA);
2019-01-19 16:52:04 +01:00
}
if (iceCoverage == 0 && iceRatio > 0) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_NO_ICE);
2019-01-19 16:52:04 +01:00
}
if (waterCoverage > 0 && waterRatio < waterCoverage) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_INSUFFICIENT_WATER);
2019-01-19 16:52:04 +01:00
}
if (lavaCoverage > 0 && lavaRatio < lavaCoverage) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_INSUFFICIENT_LAVA);
2019-01-19 16:52:04 +01:00
}
if (iceCoverage > 0 && iceRatio < iceCoverage) {
2019-01-22 00:44:01 +01:00
result.add(GreenhouseResult.FAIL_INSUFFICIENT_ICE);
2019-01-19 16:52:04 +01:00
}
2019-01-22 00:44:01 +01:00
return result;
2019-01-19 16:52:04 +01:00
}
2023-06-04 17:55:02 +02:00
private Map<Material, Integer> countBlocks(Greenhouse gh, AsyncWorldCache cache) {
Map<Material, Integer> blockCount = new EnumMap<>(Material.class);
for (int y = gh.getFloorHeight(); y< gh.getCeilingHeight();y++) {
for (int x = (int) (gh.getBoundingBox().getMinX()+1); x < gh.getBoundingBox().getMaxX(); x++) {
for (int z = (int) (gh.getBoundingBox().getMinZ()+1); z < gh.getBoundingBox().getMaxZ(); z++) {
Material t = cache.getBlockType(x, y, z);
if (!t.equals(Material.AIR)) {
blockCount.putIfAbsent(t, 0);
blockCount.merge(t, 1, Integer::sum);
}
}
}
}
return blockCount;
}
2019-01-19 16:52:04 +01:00
/**
2019-01-26 17:38:13 +01:00
* Check if block should be converted
* @param b - block to check
2019-01-19 16:52:04 +01:00
*/
public void convertBlock(Block b) {
Material bType = b.getType();
// Check if there is a block conversion for this block, as while the rest of the method wont do anything if .get() returns nothing anyway it still seems to be quite expensive
if(conversionBlocks.keySet().contains(bType)) {
for(GreenhouseBlockConversions conversion_option : conversionBlocks.get(bType)) {
// Roll the dice before bothering with checking the surrounding block as I think it's more common for greenhouses to be filled with convertable blocks and thus this dice roll wont be "wasted"
2021-08-01 08:10:07 +02:00
if(ThreadLocalRandom.current().nextDouble() < conversion_option.probability()) {
// Check if any of the adjacent blocks matches the required LocalMaterial, if there are any required LocalMaterials
2021-08-01 08:10:07 +02:00
if(conversion_option.localMaterial() != null) {
for(BlockFace adjacent_block : ADJ_BLOCKS) {
2021-08-01 08:10:07 +02:00
if(b.getRelative(adjacent_block).getType() == conversion_option.localMaterial()) {
b.setType(conversion_option.newMaterial());
break;
}
}
} else {
2021-08-01 08:10:07 +02:00
b.setType(conversion_option.newMaterial());
}
}
2020-08-16 21:49:27 +02:00
}
}
2019-01-19 16:52:04 +01:00
}
/**
* @return the type
*/
public Biome getBiome() {
return type;
}
/**
* @return true if there are blocks to convert for this biome
*/
public boolean getBlockConvert() {
return !conversionBlocks.isEmpty();
}
/**
* @return the friendly name
*/
public String getFriendlyName() {
return friendlyName;
}
/**
* @return the iceCoverage
*/
public int getIceCoverage() {
return iceCoverage;
}
/**
* @return the icon
*/
public Material getIcon() {
return icon;
}
/**
* @return the lavaCoverage
*/
public int getLavaCoverage() {
return lavaCoverage;
}
/**
* @return the mobLimit
*/
public int getMobLimit() {
return mobLimit;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the permission
*/
public String getPermission() {
return permission;
}
/**
* @return the priority
*/
2019-10-13 02:09:38 +02:00
public int getPriority() {
2019-01-19 16:52:04 +01:00
return priority;
}
2019-01-25 04:11:59 +01:00
/**
* Spawn a mob on block b if it makes sense and random change suggests it
* @param b - block
* @return true if a mob was spawned
*/
public boolean spawnMob(Block b) {
if (b.getY() == 0) {
return false;
}
// Center spawned mob
Location spawnLoc = b.getLocation().clone().add(new Vector(0.5, 0, 0.5));
2023-03-18 16:17:52 +01:00
boolean result = getRandomMob()
2019-01-25 04:11:59 +01:00
// 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))
// If spawn occurs, check if it can fit inside greenhouse
.map(m -> {
2021-08-01 08:10:07 +02:00
Entity entity = b.getWorld().spawnEntity(spawnLoc, m.mobType());
2021-09-18 19:33:48 +02:00
preventZombie(entity);
return addon
.getManager()
.getMap()
.getGreenhouse(b.getLocation()).map(gh -> {
2021-12-31 22:32:03 +01:00
if (!gh.getInternalBoundingBox().contains(entity.getBoundingBox())) {
2021-09-18 19:33:48 +02:00
entity.remove();
return false;
}
return true;
}).orElse(false);
}).orElse(false);
2023-03-18 16:17:52 +01:00
return result;
2019-01-25 04:11:59 +01:00
}
/**
* Prevent hoglins and piglins from zombifying if they spawn in the overworld
* @param entity - spawned entity
*/
private void preventZombie(Entity entity) {
if (!entity
.getWorld()
.getEnvironment()
.equals(Environment.NORMAL) ||
!Enums.getIfPresent(EntityType.class, "PIGLIN")
.isPresent()) {
return;
}
2021-08-01 08:10:07 +02:00
if (entity instanceof Piglin p) {
p.setImmuneToZombification(true);
return;
}
2021-08-01 08:10:07 +02:00
if (entity instanceof Hoglin h) {
h.setImmuneToZombification(true);
}
}
2019-01-19 16:52:04 +01:00
/**
* @return a mob that can spawn in the greenhouse
*/
2019-01-25 04:11:59 +01:00
private Optional<GreenhouseMob> getRandomMob() {
2019-01-19 16:52:04 +01:00
// Return a random mob that can spawn in the biome or empty
Double key = mobTree.ceilingKey(random.nextDouble());
return key == null ? Optional.empty() : Optional.ofNullable(mobTree.get(key));
}
private Optional<GreenhousePlant> getRandomPlant(boolean underwater) {
2019-01-19 16:52:04 +01:00
// Grow a random plant that can grow
double r = random.nextDouble();
Double key = underwater ? underwaterPlants.ceilingKey(r) : plantTree.ceilingKey(r);
return key == null ? Optional.empty() : Optional.ofNullable(underwater ? underwaterPlants.get(key) : plantTree.get(key));
2019-01-19 16:52:04 +01:00
}
/**
* @return a list of blocks that are required for this recipe
*/
public List<String> getRecipeBlocks() {
2022-03-20 16:09:45 +01:00
return requiredBlocks.entrySet().stream().map(en -> Util.prettifyText(en.getKey().toString()) + " x " + en.getValue()).toList();
2019-01-19 16:52:04 +01:00
}
/**
* @return the waterCoverage
*/
public int getWaterCoverage() {
return waterCoverage;
}
/**
* Plants a plant on block bl if it makes sense.
2021-09-18 19:33:48 +02:00
* @param block - block that can have growth
* @param underwater - if the block is underwater or not
2019-01-22 00:44:01 +01:00
* @return true if successful
2019-01-19 16:52:04 +01:00
*/
public boolean growPlant(GrowthBlock block, boolean underwater) {
Block bl = block.block();
return getRandomPlant(underwater).map(p -> {
if (bl.getY() != 0 && canGrowOn(block, p)) {
if (plantIt(bl, p)) {
bl.getWorld().spawnParticle(Particle.SNOWBALL, bl.getLocation(), 10, 2, 2, 2);
return true;
}
2019-01-19 16:52:04 +01:00
}
2019-01-22 00:44:01 +01:00
return false;
}).orElse(false);
2019-01-19 16:52:04 +01:00
}
/**
* Plants the plant
* @param bl - block to turn into a plant
* @param p - the greenhouse plant to be grown
* @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) {
// Double-height plant
bi.setHalf(Bisected.Half.BOTTOM);
BlockData dataTop = p.plantMaterial().createBlockData();
((Bisected) dataTop).setHalf(Bisected.Half.TOP);
if (bl.getRelative(BlockFace.UP).getType().equals(Material.AIR)) {
bl.setBlockData(dataBottom, false);
bl.getRelative(BlockFace.UP).setBlockData(dataTop, false);
} else {
return false; // No room
}
} else if (p.plantMaterial().equals(Material.GLOW_LICHEN)) {
return placeLichen(bl);
2023-01-02 07:29:02 +01:00
} else if (p.plantMaterial().equals(Material.COCOA)) {
return placeCocoa(bl);
} else {
if (dataBottom instanceof Waterlogged wl) {
wl.setWaterlogged(underwater);
bl.setBlockData(wl, false);
} else {
// Single height plant
bl.setBlockData(dataBottom, false);
}
}
return true;
}
2023-01-02 07:29:02 +01:00
private boolean placeCocoa(Block bl) {
// Get the source block below this one
Block b = bl.getRelative(BlockFace.DOWN);
if (!b.getType().equals(Material.JUNGLE_LOG)) {
return false;
}
// Find a spot for cocoa
BlockFace d = null;
for (BlockFace adj : SIDE_BLOCKS) {
if (b.getRelative(adj).getType().equals(Material.AIR)) {
d = adj;
break;
}
}
if (d == null) {
return false;
}
Block bb = b.getRelative(d);
bb.setType(Material.COCOA);
BlockFace opp = d.getOppositeFace();
if(bb.getBlockData() instanceof Cocoa v){
v.setFacing(opp);
bb.setBlockData(v);
bb.getState().setBlockData(v);
bb.getState().update(true);
return true;
}
return false;
}
/**
* Handles the placing of Glow Lichen. This needs to stick to a block rather than grow on it.
* If the block is set to Glow Lichen then it appears as an air block with 6 sides of lichen so
* they need to be switched off and only the side next to the block should be set.
* @param bl - block where plants would usually be placed
* @return true if successful, false if not
*/
private boolean placeLichen(Block bl) {
// Get the source block below this one
Block b = bl.getRelative(BlockFace.DOWN);
// Find a spot for licen
BlockFace d = null;
boolean waterLogged = false;
for (BlockFace adj : ADJ_BLOCKS) {
if (b.getRelative(adj).getType().equals(Material.AIR)) {
d = adj;
break;
}
// Lichen can grow under water too
if (b.getRelative(adj).getType().equals(Material.WATER)) {
d = adj;
waterLogged = true;
break;
}
}
if (d == null) {
return false;
}
Block bb = b.getRelative(d);
bb.setType(Material.GLOW_LICHEN);
BlockFace opp = d.getOppositeFace();
if(bb.getBlockData() instanceof GlowLichen v){
for (BlockFace f : v.getAllowedFaces()) {
v.setFace(f, false);
}
v.setFace(opp, true);
v.setWaterlogged(waterLogged);
bb.setBlockData(v);
bb.getState().setBlockData(v);
bb.getState().update(true);
return true;
}
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
2022-03-20 16:09:45 +01:00
if (CEILING_PLANTS.contains(p.plantMaterial()) && Boolean.TRUE.equals(block.floor())) {
return false;
}
// Underwater plants can only be placed in water and regular plants cannot be placed in water
if (block.block().getType().equals(Material.WATER)) {
if (!UNDERWATER_PLANTS.contains(p.plantMaterial())) {
return false;
}
} 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());
}
2019-01-19 16:52:04 +01:00
/**
2019-01-26 17:38:13 +01:00
* @param friendlyName - set the friendly name
2019-01-19 16:52:04 +01:00
*/
public void setFriendlyName(String friendlyName) {
this.friendlyName = friendlyName;
}
/**
2019-01-26 17:38:13 +01:00
* @param iceCoverage the ice coverage to set
2019-01-19 16:52:04 +01:00
*/
2019-01-26 17:38:13 +01:00
public void setIcecoverage(int iceCoverage) {
if (iceCoverage == 0) {
startupLog(" No Ice Allowed");
2019-01-26 17:38:13 +01:00
} else if (iceCoverage > 0) {
startupLog(" Ice > " + iceCoverage + "%");
2019-01-19 16:52:04 +01:00
}
2019-01-26 17:38:13 +01:00
this.iceCoverage = iceCoverage;
2019-01-19 16:52:04 +01:00
}
/**
* @param icon the icon to set
*/
public void setIcon(Material icon) {
this.icon = icon;
}
/**
2019-01-26 17:38:13 +01:00
* @param lavaCoverage the lava coverage to set
2019-01-19 16:52:04 +01:00
*/
2019-01-26 17:38:13 +01:00
public void setLavacoverage(int lavaCoverage) {
if (lavaCoverage == 0) {
startupLog(" No Lava Allowed");
2019-01-26 17:38:13 +01:00
} else if (lavaCoverage > 0) {
startupLog(" Lava > " + lavaCoverage + "%");
2019-01-19 16:52:04 +01:00
}
2019-01-26 17:38:13 +01:00
this.lavaCoverage = lavaCoverage;
2019-01-19 16:52:04 +01:00
}
/**
* @param mobLimit the mobLimit to set
*/
public void setMobLimit(int mobLimit) {
this.mobLimit = mobLimit;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @param permission the permission to set
*/
public void setPermission(String permission) {
this.permission = permission;
}
/**
* @param priority the priority to set
*/
public void setPriority(int priority) {
this.priority = priority;
}
/**
* @param type the type to set
*/
public void setType(Biome type) {
this.type = type;
}
/**
2019-01-26 17:38:13 +01:00
* @param waterCoverage the water coverage to set
2019-01-19 16:52:04 +01:00
*/
2019-01-26 17:38:13 +01:00
public void setWatercoverage(int waterCoverage) {
if (waterCoverage == 0) {
startupLog(" No Water Allowed");
2019-01-26 17:38:13 +01:00
} else if (waterCoverage > 0) {
startupLog(" Water > " + waterCoverage + "%");
2019-01-19 16:52:04 +01:00
}
2019-01-26 17:38:13 +01:00
this.waterCoverage = waterCoverage;
2019-01-19 16:52:04 +01:00
}
2019-01-22 00:44:01 +01:00
@Override
public int compareTo(BiomeRecipe o) {
return Integer.compare(o.getPriority(), this.getPriority());
}
2019-11-01 05:36:05 +01:00
2019-01-25 04:11:59 +01:00
/**
* @return true if this recipe has no mobs that may spawn
*/
public boolean noMobs() {
2019-11-01 05:36:05 +01:00
return mobTree.isEmpty();
2019-01-25 04:11:59 +01:00
}
/**
* @return the mob types that may spawn due to this recipe
*/
public Set<EntityType> getMobTypes() {
2021-08-01 08:10:07 +02:00
return mobTree.values().stream().map(GreenhouseMob::mobType).collect(Collectors.toSet());
2019-01-25 04:11:59 +01:00
}
2023-03-01 17:26:34 +01:00
/**
* Set the maximum number of mobs in a greenhouse
* @param maxMob maximum
*/
public void setMaxMob(int maxMob) {
this.maxMob = maxMob;
}
/**
* @return the maxMob
*/
public int getMaxMob() {
return maxMob;
}
2019-11-01 05:36:05 +01:00
2019-01-19 16:52:04 +01:00
}