2019-01-19 16:52:04 +01:00
package world.bentobox.greenhouses.greenhouse ;
2021-09-18 19:12:47 +02:00
import java.util.ArrayList ;
2019-01-19 16:52:04 +01:00
import java.util.Arrays ;
2022-12-30 03:59:50 +01:00
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 ;
2021-01-17 17:41:50 +01:00
import java.util.concurrent.CompletableFuture ;
2021-04-19 16:26:43 +02:00
import java.util.concurrent.ThreadLocalRandom ;
2019-01-19 16:52:04 +01:00
import java.util.stream.Collectors ;
2021-01-17 17:41:50 +01:00
import org.bukkit.Bukkit ;
2019-10-18 07:37:27 +02:00
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 ;
2020-11-16 00:54:06 +01:00
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 ;
2019-09-20 01:21:43 +02:00
import org.bukkit.block.data.Bisected ;
import org.bukkit.block.data.BlockData ;
2022-12-30 03:59:50 +01:00
import org.bukkit.block.data.Waterlogged ;
2023-01-02 07:29:02 +01:00
import org.bukkit.block.data.type.Cocoa ;
2022-12-29 18:57:21 +01:00
import org.bukkit.block.data.type.GlowLichen ;
2019-10-18 07:37:27 +02:00
import org.bukkit.entity.Entity ;
2019-01-19 16:52:04 +01:00
import org.bukkit.entity.EntityType ;
2020-11-16 00:54:06 +01:00
import org.bukkit.entity.Hoglin ;
import org.bukkit.entity.Piglin ;
2019-10-18 07:37:27 +02:00
import org.bukkit.util.Vector ;
2019-01-19 16:52:04 +01:00
2020-11-16 00:54:06 +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 ;
2021-09-18 19:12:47 +02:00
import world.bentobox.greenhouses.managers.EcoSystemManager.GrowthBlock ;
2019-01-22 00:44:01 +01:00
import world.bentobox.greenhouses.managers.GreenhouseManager.GreenhouseResult ;
2021-01-17 17:41:50 +01:00
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 " ;
2019-05-09 00:21:11 +02:00
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 ;
2021-09-18 19:12:47 +02:00
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 ) ;
}
2020-08-23 02:55:38 +02:00
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 ) ;
2022-12-30 03:59:50 +01:00
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 ) ;
2022-12-30 03:59:50 +01:00
/ * *
* Tree map of plants
* /
2019-01-26 17:38:13 +01:00
private final TreeMap < Double , GreenhousePlant > plantTree = new TreeMap < > ( ) ;
2022-12-30 03:59:50 +01:00
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-10-12 18:31:51 +02:00
2019-01-19 16:52:04 +01:00
2021-08-02 00:26:21 +02:00
/ * *
* Create a degenerate recipe with nothing in it
* /
2019-05-09 00:21:11 +02:00
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
2020-01-30 18:52:53 +01: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 ) ) ;
2020-01-30 18:52:53 +01:00
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
2021-04-19 16:26:43 +02:00
* @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
* /
2020-11-16 00:35:59 +01:00
public boolean addMobs ( EntityType mobType , double mobProbability , Material mobSpawnOn ) {
2020-01-30 18:52:53 +01:00
startupLog ( " " + mobProbability + CHANCE_FOR + Util . prettifyText ( mobType . toString ( ) ) + " to spawn on " + Util . prettifyText ( mobSpawnOn . toString ( ) ) + " . " ) ;
2020-11-16 00:35:59 +01:00
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
* /
2020-11-16 00:35:59 +01:00
public boolean addPlants ( Material plantMaterial , double plantProbability , Material plantGrowOn ) {
double probability = plantProbability / 100 ;
2022-12-30 03:59:50 +01:00
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
2022-12-30 03:59:50 +01:00
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
2022-12-30 03:59:50 +01:00
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
}
2020-01-30 18:52:53 +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 ) ;
2020-01-30 18:52:53 +01:00
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
* /
2021-01-17 17:41:50 +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 ) {
AsyncWorldCache cache = new AsyncWorldCache ( gh . getWorld ( ) ) ;
2019-01-22 00:44:01 +01:00
Set < GreenhouseResult > result = new HashSet < > ( ) ;
long area = gh . getArea ( ) ;
2019-11-01 05:36:05 +01:00
Map < Material , Integer > blockCount = new EnumMap < > ( Material . class ) ;
2021-01-17 17:41:50 +01:00
2019-01-19 16:52:04 +01:00
// Look through the greenhouse and count what is in there
2019-01-22 00:44:01 +01:00
for ( int y = gh . getFloorHeight ( ) ; y < gh . getCeilingHeight ( ) ; y + + ) {
2019-05-09 00:21:11 +02:00
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 + + ) {
2021-01-17 17:41:50 +01:00
Material t = cache . getBlockType ( x , y , z ) ;
2019-11-01 05:36:05 +01:00
if ( ! t . equals ( Material . AIR ) ) {
blockCount . putIfAbsent ( t , 0 ) ;
blockCount . merge ( t , 1 , Integer : : sum ) ;
2019-01-19 16:52:04 +01:00
}
}
}
}
// Calculate % water, ice and lava ratios
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
}
// Compare to the required blocks
2019-10-12 03:52:32 +02:00
Map < Material , Integer > missingBlocks = requiredBlocks . entrySet ( ) . stream ( ) . collect ( Collectors . toMap ( Map . Entry : : getKey , e - > e . getValue ( ) - blockCount . getOrDefault ( e . getKey ( ) , 0 ) ) ) ;
2019-01-19 16:52:04 +01:00
// Remove any entries that are 0 or less
missingBlocks . values ( ) . removeIf ( v - > v < = 0 ) ;
2019-10-12 03:52:32 +02:00
if ( ! missingBlocks . isEmpty ( ) ) {
result . add ( GreenhouseResult . FAIL_INSUFFICIENT_BLOCKS ) ;
gh . setMissingBlocks ( missingBlocks ) ;
}
2021-01-17 17:41:50 +01:00
// Return to main thread to complete
Bukkit . getScheduler ( ) . runTask ( addon . getPlugin ( ) , ( ) - > r . complete ( result ) ) ;
2019-01-22 00:44:01 +01:00
return result ;
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
* /
2021-04-19 16:26:43 +02: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 ( ) ) {
2021-04-19 16:26:43 +02:00
// 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 ) {
2021-04-19 16:26:43 +02:00
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 ( ) ) ;
2021-04-19 16:26:43 +02:00
break ;
}
}
} else {
2021-08-01 08:10:07 +02:00
b . setType ( conversion_option . newMaterial ( ) ) ;
2021-04-19 16:26:43 +02:00
}
}
2020-08-16 21:49:27 +02:00
}
2021-04-19 16:26:43 +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 ;
}
2019-10-18 07:37:27 +02:00
// 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
2022-12-30 03:59:50 +01:00
. filter ( m - > Optional . of ( m . mobSpawnOn ( ) )
. map ( b . getRelative ( BlockFace . DOWN ) . getType ( ) : : equals )
. orElse ( true ) )
2019-10-18 07:37:27 +02:00
// 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 ) ;
2019-10-18 07:37:27 +02:00
} ) . orElse ( false ) ;
2023-03-18 16:17:52 +01:00
return result ;
2019-01-25 04:11:59 +01:00
}
2020-11-16 00:54:06 +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 ) {
2020-11-16 00:54:06 +01:00
p . setImmuneToZombification ( true ) ;
return ;
}
2021-08-01 08:10:07 +02:00
if ( entity instanceof Hoglin h ) {
2020-11-16 00:54:06 +01:00
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 ) ) ;
}
2022-12-30 03:59:50 +01:00
private Optional < GreenhousePlant > getRandomPlant ( boolean underwater ) {
2019-01-19 16:52:04 +01:00
// Grow a random plant that can grow
2019-01-24 18:18:05 +01:00
double r = random . nextDouble ( ) ;
2022-12-30 03:59:50 +01:00
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
2022-12-30 03:59:50 +01:00
* @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
* /
2022-12-30 03:59:50 +01:00
public boolean growPlant ( GrowthBlock block , boolean underwater ) {
2021-09-18 19:12:47 +02:00
Block bl = block . block ( ) ;
2022-12-30 03:59:50 +01:00
return getRandomPlant ( underwater ) . map ( p - > {
2021-09-18 19:12:47 +02:00
if ( bl . getY ( ) ! = 0 & & canGrowOn ( block , p ) ) {
2022-12-30 03:59:50 +01:00
if ( plantIt ( bl , p ) ) {
bl . getWorld ( ) . spawnParticle ( Particle . SNOWBALL , bl . getLocation ( ) , 10 , 2 , 2 , 2 ) ;
return true ;
2019-09-20 01:21:43 +02:00
}
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
}
2022-12-29 18:57:21 +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 ) {
2022-12-30 03:59:50 +01:00
boolean underwater = bl . getType ( ) . equals ( Material . WATER ) ;
2021-09-18 19:12:47 +02:00
BlockData dataBottom = p . plantMaterial ( ) . createBlockData ( ) ;
2022-12-29 18:57:21 +01:00
// Check if this is a double-height plant
2021-09-18 19:12:47 +02:00
if ( dataBottom instanceof Bisected bi ) {
2022-12-29 18:57:21 +01:00
// Double-height plant
2021-09-18 19:12:47 +02:00
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
}
2022-12-29 18:57:21 +01:00
} 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 ) ;
2021-09-18 19:12:47 +02:00
} else {
2022-12-30 03:59:50 +01:00
if ( dataBottom instanceof Waterlogged wl ) {
wl . setWaterlogged ( underwater ) ;
bl . setBlockData ( wl , false ) ;
} else {
// Single height plant
bl . setBlockData ( dataBottom , false ) ;
}
2021-09-18 19:12:47 +02:00
}
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 ;
}
2022-12-29 18:57:21 +01:00
/ * *
* 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 ;
}
2022-12-30 03:59:50 +01:00
/ * *
* 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
* /
2021-09-18 19:12:47 +02:00
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 ( ) ) ) {
2021-09-18 19:12:47 +02:00
return false ;
}
2022-12-30 03:59:50 +01:00
// 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 ( ) ) ;
2021-09-18 19:12:47 +02:00
}
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 ) {
2020-01-30 18:52:53 +01:00
startupLog ( " No Ice Allowed " ) ;
2019-01-26 17:38:13 +01:00
} else if ( iceCoverage > 0 ) {
2020-01-30 18:52:53 +01:00
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 ) {
2020-01-30 18:52:53 +01:00
startupLog ( " No Lava Allowed " ) ;
2019-01-26 17:38:13 +01:00
} else if ( lavaCoverage > 0 ) {
2020-01-30 18:52:53 +01:00
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 ) {
2020-01-30 18:52:53 +01:00
startupLog ( " No Water Allowed " ) ;
2019-01-26 17:38:13 +01:00
} else if ( waterCoverage > 0 ) {
2020-01-30 18:52:53 +01:00
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
}
2019-05-09 00:21:11 +02: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
}