diff --git a/src/java/mmm/core/CRegistry.java b/src/java/mmm/core/CRegistry.java
index 1237085..c25e101 100644
--- a/src/java/mmm/core/CRegistry.java
+++ b/src/java/mmm/core/CRegistry.java
@@ -8,6 +8,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 
 import mmm.Mmm;
+import mmm.core.api.I_FloraRegistrar;
 import mmm.core.api.I_OreGenerationRegistrar;
 import mmm.core.api.I_RecipeRegistrar;
 import mmm.core.api.I_RequiresClientInit;
@@ -64,6 +65,8 @@ public class CRegistry
 
 	private static final HashSet< I_RecipeRegistrar > RECIPE_REGISTRARS = new HashSet<>( );
 	private static final HashSet< I_OreGenerationRegistrar > ORE_GEN_REGISTRARS = new HashSet<>( );
+	private static final HashSet< I_FloraRegistrar > FLORA_REGISTRARS = new HashSet<>( );
+
 	private static final HashSet< Item > ITEMS = new HashSet< Item >( );
 	private static final HashSet< Block > BLOCKS = new HashSet< Block >( );
 
@@ -114,7 +117,7 @@ public class CRegistry
 	{
 		if ( CRegistry.ITEMS.add( item ) ) {
 			GameRegistry.register( item );
-			CRegistry.addRecipeRegistrar( item );
+			CRegistry.addRegistrar( item );
 			if ( item instanceof I_RequiresClientInit ) {
 				CRegistry.ITEMS_CLIENT_INIT.add( item );
 			}
@@ -139,8 +142,7 @@ public class CRegistry
 	{
 		if ( CRegistry.BLOCKS.add( block ) ) {
 			GameRegistry.register( block );
-			CRegistry.addRecipeRegistrar( block );
-			CRegistry.addOreGenerationRegistrar( block );
+			CRegistry.addRegistrar( block );
 			if ( block instanceof I_RequiresClientPreInit ) {
 				CRegistry.BLOCKS_CLIENT_PREINIT.add( block );
 			}
@@ -186,6 +188,14 @@ public class CRegistry
 	}
 
 
+	public static void addRegistrar( final Object object )
+	{
+		CRegistry.addRecipeRegistrar( object );
+		CRegistry.addOreGenerationRegistrar( object );
+		CRegistry.addFloraRegistrar( object );
+	}
+
+
 	public static void addRecipeRegistrar( final Object object )
 	{
 		if ( object instanceof I_RecipeRegistrar ) {
@@ -202,12 +212,26 @@ public class CRegistry
 	}
 
 
+	public static void addFloraRegistrar( final Object object )
+	{
+		if ( object instanceof I_FloraRegistrar ) {
+			CRegistry.FLORA_REGISTRARS.add( (I_FloraRegistrar) object );
+		}
+	}
+
+
 	public static Collection< I_OreGenerationRegistrar > getOreGenerationRegistrars( )
 	{
 		return Collections.unmodifiableCollection( CRegistry.ORE_GEN_REGISTRARS );
 	}
 
 
+	public static Collection< I_FloraRegistrar > getFloraRegistrars( )
+	{
+		return Collections.unmodifiableCollection( CRegistry.FLORA_REGISTRARS );
+	}
+
+
 	public static void registerRecipes( )
 	{
 		for ( final I_RecipeRegistrar registrar : CRegistry.RECIPE_REGISTRARS ) {
diff --git a/src/java/mmm/core/api/I_FloraRegistrar.java b/src/java/mmm/core/api/I_FloraRegistrar.java
new file mode 100644
index 0000000..722c32d
--- /dev/null
+++ b/src/java/mmm/core/api/I_FloraRegistrar.java
@@ -0,0 +1,15 @@
+package mmm.core.api;
+
+
+import java.util.List;
+
+import mmm.world.gen.WGFloraParameters;
+
+
+
+public interface I_FloraRegistrar
+{
+
+	public void getFloraGeneration( List< WGFloraParameters > output );
+
+}
diff --git a/src/java/mmm/core/api/world/I_BiomeWithFlora.java b/src/java/mmm/core/api/world/I_BiomeWithFlora.java
new file mode 100644
index 0000000..ca3be90
--- /dev/null
+++ b/src/java/mmm/core/api/world/I_BiomeWithFlora.java
@@ -0,0 +1,15 @@
+package mmm.core.api.world;
+
+
+import java.util.List;
+
+import mmm.world.gen.WGFloraParameters;
+
+
+
+public interface I_BiomeWithFlora
+{
+
+	public void addFloraParameters( List< WGFloraParameters > output );
+
+}
diff --git a/src/java/mmm/core/api/world/I_LocationCheck.java b/src/java/mmm/core/api/world/I_LocationCheck.java
index 3229037..4a2af86 100644
--- a/src/java/mmm/core/api/world/I_LocationCheck.java
+++ b/src/java/mmm/core/api/world/I_LocationCheck.java
@@ -14,45 +14,19 @@ public interface I_LocationCheck
 
 	default I_LocationCheck and( final I_LocationCheck other )
 	{
-		final I_LocationCheck self = this;
-		return new I_LocationCheck( ) {
-			@Override
-			public boolean checkLocation( final World world , final int chunkX , final int chunkZ ,
-					final IChunkProvider provider )
-			{
-				return self.checkLocation( world , chunkX , chunkZ , provider )
-						&& other.checkLocation( world , chunkX , chunkZ , provider );
-			}
-		};
+		return ( w , x , z , p ) -> this.checkLocation( w , x , z , p ) && other.checkLocation( w , x , z , p );
 	}
 
 
 	default I_LocationCheck or( final I_LocationCheck other )
 	{
-		final I_LocationCheck self = this;
-		return new I_LocationCheck( ) {
-			@Override
-			public boolean checkLocation( final World world , final int chunkX , final int chunkZ ,
-					final IChunkProvider provider )
-			{
-				return self.checkLocation( world , chunkX , chunkZ , provider )
-						|| other.checkLocation( world , chunkX , chunkZ , provider );
-			}
-		};
+		return ( w , x , z , p ) -> this.checkLocation( w , x , z , p ) || other.checkLocation( w , x , z , p );
 	}
 
 
 	default I_LocationCheck invert( )
 	{
-		final I_LocationCheck self = this;
-		return new I_LocationCheck( ) {
-			@Override
-			public boolean checkLocation( final World world , final int chunkX , final int chunkZ ,
-					final IChunkProvider provider )
-			{
-				return !self.checkLocation( world , chunkX , chunkZ , provider );
-			}
-		};
+		return ( w , x , z , p ) -> !this.checkLocation( w , x , z , p );
 	}
 
 }
diff --git a/src/java/mmm/plants/PTomato.java b/src/java/mmm/plants/PTomato.java
index 2bac0ff..8586cef 100644
--- a/src/java/mmm/plants/PTomato.java
+++ b/src/java/mmm/plants/PTomato.java
@@ -8,9 +8,14 @@ import java.util.Random;
 import javax.annotation.Nullable;
 
 import mmm.core.CRegistry;
+import mmm.core.api.I_FloraRegistrar;
 import mmm.core.api.I_RecipeRegistrar;
 import mmm.core.api.blocks.I_StateMapperProvider;
 import mmm.utils.UMaths;
+import mmm.world.WLocation;
+import mmm.world.WLocationRainfall;
+import mmm.world.WLocationTemperature;
+import mmm.world.gen.WGFloraParameters;
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockBush;
 import net.minecraft.block.IGrowable;
@@ -40,7 +45,7 @@ import net.minecraftforge.fml.common.registry.GameRegistry;
 
 
 public class PTomato
-		implements I_RecipeRegistrar
+		implements I_RecipeRegistrar , I_FloraRegistrar
 {
 	public static final PropertyInteger AGE = PropertyInteger.create( "age" , 0 , 15 );
 	public static final PropertyInteger SIZE = PropertyInteger.create( "size" , 0 , 3 );
@@ -401,7 +406,7 @@ public class PTomato
 		CRegistry.addBlock( this.CROP = new Plant( ) , null );
 		this.SEEDS = PPlantsHelper.makeSeeds( "tomato" , this.CROP );
 		this.FRUIT = PPlantsHelper.makeFruit( "tomato" , 2 , .15f );
-		CRegistry.addRecipeRegistrar( this );
+		CRegistry.addRegistrar( this );
 	}
 
 
@@ -412,4 +417,23 @@ public class PTomato
 				new ItemStack( this.FRUIT ) );
 	}
 
+
+	@Override
+	public void getFloraGeneration( final List< WGFloraParameters > output )
+	{
+		output.add( new WGFloraParameters( this.WILD.getDefaultState( ) , 1 ,
+				WLocation.inOverworld( )//
+						.and( new WLocationTemperature( 0.4f , 1f ) ) //
+						.and( new WLocationRainfall( 0.2f , 0.8f ) ) ) );
+		output.add( new WGFloraParameters( this.WILD.getDefaultState( ) , 1 ,
+				WLocation.inOverworld( )//
+						.and( new WLocationTemperature( 0.7f , 1f ) ) //
+						.and( new WLocationRainfall( 0.3f , 0.5f ) ) ) );
+		output.add( new WGFloraParameters( //
+				this.WILD.getDefaultState( ).withProperty( PTomato.WITH_FRUITS , true ) , 1 ,
+				WLocation.inOverworld( )//
+						.and( new WLocationTemperature( 0.8f , 0.9f ) ) //
+						.and( new WLocationRainfall( 0.4f , 0.5f ) ) ) );
+	}
+
 }
diff --git a/src/java/mmm/world/WDefaultGenWatcher.java b/src/java/mmm/world/WDefaultGenWatcher.java
index aabfe09..1deffdf 100644
--- a/src/java/mmm/world/WDefaultGenWatcher.java
+++ b/src/java/mmm/world/WDefaultGenWatcher.java
@@ -6,11 +6,13 @@ import java.util.Random;
 import mmm.core.api.world.I_DefaultPopulateHandler;
 import mmm.core.api.world.I_TrappedBiome;
 import mmm.world.gen.WGBasalt;
+import mmm.world.gen.WGFlora;
 import mmm.world.gen.WGTrapBlocks;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.world.biome.Biome;
 import net.minecraft.world.biome.BiomeDesert;
 import net.minecraft.world.biome.BiomeSwamp;
+import net.minecraftforge.event.terraingen.DecorateBiomeEvent;
 import net.minecraftforge.event.terraingen.PopulateChunkEvent;
 import net.minecraftforge.fml.common.eventhandler.Event.Result;
 import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -19,6 +21,8 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
 
 public class WDefaultGenWatcher
 {
+	private final WGFlora floraGenerator = new WGFlora( );
+
 
 	@SubscribeEvent
 	public void onPopulate( final PopulateChunkEvent.Populate event )
@@ -61,4 +65,12 @@ public class WDefaultGenWatcher
 		traps.generate( world , rand , bp );
 	}
 
+
+	@SubscribeEvent
+	public void onBiomeDecorate( DecorateBiomeEvent.Decorate event )
+	{
+		if ( event.getType( ) == DecorateBiomeEvent.Decorate.EventType.GRASS ) {
+			this.floraGenerator.generate( event.getWorld( ) , event.getRand( ) , event.getPos( ) );
+		}
+	}
 }
diff --git a/src/java/mmm/world/WLocation.java b/src/java/mmm/world/WLocation.java
index 010238d..148aeda 100644
--- a/src/java/mmm/world/WLocation.java
+++ b/src/java/mmm/world/WLocation.java
@@ -8,6 +8,8 @@ import net.minecraft.world.biome.Biome;
 
 public class WLocation
 {
+	private static final I_LocationCheck ANYWHERE = ( w , x , z , p ) -> true;
+
 	private static I_LocationCheck inOverworld;
 	private static I_LocationCheck inTheNether;
 
@@ -40,4 +42,10 @@ public class WLocation
 	{
 		return new WLocationInBiomeClass<>( biomeType );
 	}
+
+
+	public static I_LocationCheck anywhere( )
+	{
+		return WLocation.ANYWHERE;
+	}
 }
diff --git a/src/java/mmm/world/WLocationRainfall.java b/src/java/mmm/world/WLocationRainfall.java
new file mode 100644
index 0000000..e5f701c
--- /dev/null
+++ b/src/java/mmm/world/WLocationRainfall.java
@@ -0,0 +1,38 @@
+package mmm.world;
+
+
+import mmm.core.api.world.I_LocationCheck;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.IChunkProvider;
+
+
+
+public class WLocationRainfall
+		implements I_LocationCheck
+{
+	public final float minRainfall;
+	public final float maxRainfall;
+
+
+	public WLocationRainfall( final float minRainfall , final float maxRainfall )
+	{
+		if ( maxRainfall < minRainfall ) {
+			throw new IllegalArgumentException( "minimal rainfall is higher than maximal rainfall" );
+		}
+		this.minRainfall = minRainfall;
+		this.maxRainfall = maxRainfall;
+	}
+
+
+	@Override
+	public boolean checkLocation( final World world , final int chunkX , final int chunkZ ,
+			final IChunkProvider provider )
+	{
+		final Biome biome = world.getBiomeGenForCoords( new BlockPos( chunkX * 16 , 0 , chunkZ * 16 ) );
+		final float rf = biome.getRainfall( );
+		return rf >= this.minRainfall && rf <= this.maxRainfall;
+	}
+
+}
diff --git a/src/java/mmm/world/WLocationTemperature.java b/src/java/mmm/world/WLocationTemperature.java
new file mode 100644
index 0000000..e34d3df
--- /dev/null
+++ b/src/java/mmm/world/WLocationTemperature.java
@@ -0,0 +1,38 @@
+package mmm.world;
+
+
+import mmm.core.api.world.I_LocationCheck;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.chunk.IChunkProvider;
+
+
+
+public class WLocationTemperature
+		implements I_LocationCheck
+{
+	public final float minTemperature;
+	public final float maxTemperature;
+
+
+	public WLocationTemperature( final float minTemperature , final float maxTemperature )
+	{
+		if ( maxTemperature < minTemperature ) {
+			throw new IllegalArgumentException( "minimal temperature is higher than maximal temperature" );
+		}
+		this.minTemperature = minTemperature;
+		this.maxTemperature = maxTemperature;
+	}
+
+
+	@Override
+	public boolean checkLocation( final World world , final int chunkX , final int chunkZ ,
+			final IChunkProvider provider )
+	{
+		final Biome biome = world.getBiomeGenForCoords( new BlockPos( chunkX * 16 , 0 , chunkZ * 16 ) );
+		final float t = biome.getTemperature( );
+		return t >= this.minTemperature && t <= this.maxTemperature;
+	}
+
+}
diff --git a/src/java/mmm/world/gen/WGFlora.java b/src/java/mmm/world/gen/WGFlora.java
new file mode 100644
index 0000000..141b2b5
--- /dev/null
+++ b/src/java/mmm/world/gen/WGFlora.java
@@ -0,0 +1,109 @@
+package mmm.world.gen;
+
+
+import java.util.ArrayList;
+import java.util.Random;
+
+import mmm.core.CRegistry;
+import mmm.core.api.I_FloraRegistrar;
+import mmm.core.api.world.I_BiomeWithFlora;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+
+
+public class WGFlora
+		extends WorldGenerator
+{
+
+	private final ArrayList< WGFloraParameters > allConditions;
+
+
+	public WGFlora( )
+	{
+		final ArrayList< WGFloraParameters > conditions = new ArrayList<>( );
+		for ( final I_FloraRegistrar registrar : CRegistry.getFloraRegistrars( ) ) {
+			registrar.getFloraGeneration( conditions );
+		}
+		this.allConditions = conditions;
+	}
+
+
+	@Override
+	public boolean generate( final World worldIn , final Random rand , final BlockPos position )
+	{
+		final ArrayList< WGFloraParameters > candidates = this.getValidFlora( worldIn , position );
+		final int nCandidates = candidates.size( );
+		if ( nCandidates == 0 ) {
+			return false;
+		}
+
+		for ( int i = 0 ; i < nCandidates ; i++ ) {
+			this.tryGenerate( worldIn , rand , position , candidates.get( i ) );
+		}
+		return true;
+	}
+
+
+	private ArrayList< WGFloraParameters > getValidFlora( final World worldIn , final BlockPos position )
+	{
+		final ArrayList< WGFloraParameters > output = new ArrayList<>( );
+		for ( final WGFloraParameters item : this.allConditions ) {
+			if ( item.location.checkLocation( worldIn , position.getX( ) >> 4 , position.getZ( ) >> 4 ,
+					worldIn.getChunkProvider( ) ) ) {
+				output.add( item );
+			}
+		}
+
+		final Biome biome = worldIn.getBiomeGenForCoords( position );
+		if ( biome instanceof I_BiomeWithFlora ) {
+			( (I_BiomeWithFlora) biome ).addFloraParameters( output );
+		}
+
+		return output;
+	}
+
+
+	private void tryGenerate( final World worldIn , final Random rand , final BlockPos position ,
+			final WGFloraParameters parameters )
+	{
+		for ( int i = 0 ; i < parameters.perChunk ; ++i ) {
+			final int x = rand.nextInt( 16 ) + 8;
+			final int z = rand.nextInt( 16 ) + 8;
+
+			final int height = worldIn.getHeight( position.add( x , 0 , z ) ).getY( ) * 2;
+			if ( height <= 0 ) {
+				continue;
+			}
+
+			this.tryGeneratePlant( worldIn , rand , position.add( x , rand.nextInt( height ) , z ) , parameters );
+		}
+	}
+
+
+	private void tryGeneratePlant( final World worldIn , final Random rand , BlockPos position ,
+			final WGFloraParameters parameters )
+	{
+		IBlockState bs = worldIn.getBlockState( position );
+		while ( ( bs.getBlock( ).isAir( bs , worldIn , position )
+				|| bs.getBlock( ).isLeaves( bs , worldIn , position ) ) && position.getY( ) > 0 ) {
+			position = position.down( );
+			bs = worldIn.getBlockState( position );
+		}
+
+		for ( int i = 0 ; i < 128 ; ++i ) {
+			final BlockPos blockpos = position.add( //
+					rand.nextInt( 8 ) - rand.nextInt( 8 ) , //
+					rand.nextInt( 4 ) - rand.nextInt( 4 ) , //
+					rand.nextInt( 8 ) - rand.nextInt( 8 ) );
+			if ( parameters.placementCheck.test( worldIn , blockpos ) ) {
+				worldIn.setBlockState( blockpos , parameters.floraType , 2 );
+				break;
+			}
+		}
+	}
+
+}
diff --git a/src/java/mmm/world/gen/WGFloraParameters.java b/src/java/mmm/world/gen/WGFloraParameters.java
new file mode 100644
index 0000000..0dff406
--- /dev/null
+++ b/src/java/mmm/world/gen/WGFloraParameters.java
@@ -0,0 +1,62 @@
+package mmm.world.gen;
+
+
+import java.util.function.BiPredicate;
+
+import mmm.core.api.world.I_LocationCheck;
+import mmm.world.WLocation;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockBush;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+
+
+public class WGFloraParameters
+{
+
+	private static BiPredicate< World , BlockPos > getDefaultPlacementCheck( final IBlockState bs )
+	{
+		final Block block = bs.getBlock( );
+		if ( block instanceof BlockBush ) {
+			return ( w , p ) -> w.isAirBlock( p ) && ( (BlockBush) block ).canBlockStay( w , p , bs );
+		}
+		return ( w , p ) -> w.isAirBlock( p ) && !w.isAirBlock( p.down( ) );
+	}
+
+	public final IBlockState floraType;
+	public final int perChunk;
+	public final I_LocationCheck location;
+	public final BiPredicate< World , BlockPos > placementCheck;
+
+
+	public WGFloraParameters( final IBlockState floraType , final int perChunk )
+	{
+		this( floraType , perChunk , WLocation.anywhere( ) , WGFloraParameters.getDefaultPlacementCheck( floraType ) );
+	}
+
+
+	public WGFloraParameters( final IBlockState floraType , final int perChunk ,
+			final BiPredicate< World , BlockPos > placementCheck )
+	{
+		this( floraType , perChunk , WLocation.anywhere( ) , placementCheck );
+	}
+
+
+	public WGFloraParameters( final IBlockState floraType , final int perChunk , final I_LocationCheck location )
+	{
+		this( floraType , perChunk , location , WGFloraParameters.getDefaultPlacementCheck( floraType ) );
+	}
+
+
+	public WGFloraParameters( final IBlockState floraType , final int perChunk , final I_LocationCheck location ,
+			final BiPredicate< World , BlockPos > placementCheck )
+	{
+		this.floraType = floraType;
+		this.perChunk = perChunk;
+		this.location = location;
+		this.placementCheck = placementCheck;
+	}
+
+}